You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-26 17:03:12 +03:00
Merge remote-tracking branch 'origin/develop' into dbkr/cross_signing
This commit is contained in:
295
CHANGELOG.md
295
CHANGELOG.md
@@ -1,3 +1,298 @@
|
|||||||
|
Changes in [2.4.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.2) (2019-10-18)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.2-rc.1...v2.4.2)
|
||||||
|
|
||||||
|
* No changes since v2.4.2-rc.1
|
||||||
|
|
||||||
|
Changes in [2.4.2-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.2-rc.1) (2019-10-09)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.1...v2.4.2-rc.1)
|
||||||
|
|
||||||
|
* Log state of Olm sessions
|
||||||
|
[\#1047](https://github.com/matrix-org/matrix-js-sdk/pull/1047)
|
||||||
|
* Add method to get access to all timelines
|
||||||
|
[\#1048](https://github.com/matrix-org/matrix-js-sdk/pull/1048)
|
||||||
|
|
||||||
|
Changes in [2.4.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.1) (2019-10-01)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.0...v2.4.1)
|
||||||
|
|
||||||
|
* Upgrade deps
|
||||||
|
[\#1046](https://github.com/matrix-org/matrix-js-sdk/pull/1046)
|
||||||
|
* Ignore crypto events with no content
|
||||||
|
[\#1043](https://github.com/matrix-org/matrix-js-sdk/pull/1043)
|
||||||
|
|
||||||
|
Changes in [2.4.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.0) (2019-09-27)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.4.0-rc.1...v2.4.0)
|
||||||
|
|
||||||
|
* Clean Yarn cache during release
|
||||||
|
[\#1045](https://github.com/matrix-org/matrix-js-sdk/pull/1045)
|
||||||
|
|
||||||
|
Changes in [2.4.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.4.0-rc.1) (2019-09-25)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.3.2...v2.4.0-rc.1)
|
||||||
|
|
||||||
|
* Remove id_server from creds for interactive auth
|
||||||
|
[\#1044](https://github.com/matrix-org/matrix-js-sdk/pull/1044)
|
||||||
|
* Remove IS details from requestToken to HS
|
||||||
|
[\#1041](https://github.com/matrix-org/matrix-js-sdk/pull/1041)
|
||||||
|
* Add support for sending MSISDN tokens to alternate URLs
|
||||||
|
[\#1040](https://github.com/matrix-org/matrix-js-sdk/pull/1040)
|
||||||
|
* Add separate 3PID add and bind APIs
|
||||||
|
[\#1038](https://github.com/matrix-org/matrix-js-sdk/pull/1038)
|
||||||
|
* Bump eslint-utils from 1.4.0 to 1.4.2
|
||||||
|
[\#1037](https://github.com/matrix-org/matrix-js-sdk/pull/1037)
|
||||||
|
* Handle WebRTC security errors as non-fatal
|
||||||
|
[\#1036](https://github.com/matrix-org/matrix-js-sdk/pull/1036)
|
||||||
|
* Check for r0.6.0 support in addition to unstable feature flags
|
||||||
|
[\#1035](https://github.com/matrix-org/matrix-js-sdk/pull/1035)
|
||||||
|
* Update room members on member event redaction
|
||||||
|
[\#1030](https://github.com/matrix-org/matrix-js-sdk/pull/1030)
|
||||||
|
* Support hidden read receipts
|
||||||
|
[\#1028](https://github.com/matrix-org/matrix-js-sdk/pull/1028)
|
||||||
|
* Do 3pid lookups in lowercase
|
||||||
|
[\#1029](https://github.com/matrix-org/matrix-js-sdk/pull/1029)
|
||||||
|
* Add Synapse admin functions for deactivating a user
|
||||||
|
[\#1027](https://github.com/matrix-org/matrix-js-sdk/pull/1027)
|
||||||
|
* Fix addPendingEvent with pending event order == chronological
|
||||||
|
[\#1026](https://github.com/matrix-org/matrix-js-sdk/pull/1026)
|
||||||
|
* Add AutoDiscovery.getRawClientConfig() for easy .well-known lookups
|
||||||
|
[\#1024](https://github.com/matrix-org/matrix-js-sdk/pull/1024)
|
||||||
|
* Don't convert errors to JSON if they are JSON already
|
||||||
|
[\#1025](https://github.com/matrix-org/matrix-js-sdk/pull/1025)
|
||||||
|
* Send id_access_token to HS for use in proxied IS requests
|
||||||
|
[\#1022](https://github.com/matrix-org/matrix-js-sdk/pull/1022)
|
||||||
|
* Clean up JSON handling in identity server requests
|
||||||
|
[\#1023](https://github.com/matrix-org/matrix-js-sdk/pull/1023)
|
||||||
|
* Use the v2 (hashed) lookup for identity server queries
|
||||||
|
[\#1021](https://github.com/matrix-org/matrix-js-sdk/pull/1021)
|
||||||
|
* Add getIdServer() & doesServerRequireIdServerParam()
|
||||||
|
[\#1018](https://github.com/matrix-org/matrix-js-sdk/pull/1018)
|
||||||
|
* Make requestToken endpoints work without ID Server
|
||||||
|
[\#1019](https://github.com/matrix-org/matrix-js-sdk/pull/1019)
|
||||||
|
* Fix setIdentityServer
|
||||||
|
[\#1016](https://github.com/matrix-org/matrix-js-sdk/pull/1016)
|
||||||
|
* Change ICE fallback server and make fallback opt-in
|
||||||
|
[\#1015](https://github.com/matrix-org/matrix-js-sdk/pull/1015)
|
||||||
|
* Throw an exception if trying to do an ID server request with no ID server
|
||||||
|
[\#1014](https://github.com/matrix-org/matrix-js-sdk/pull/1014)
|
||||||
|
* Add setIdentityServerUrl
|
||||||
|
[\#1013](https://github.com/matrix-org/matrix-js-sdk/pull/1013)
|
||||||
|
* Add matrix base API to report an event
|
||||||
|
[\#1011](https://github.com/matrix-org/matrix-js-sdk/pull/1011)
|
||||||
|
* Fix POST body for v2 IS requests
|
||||||
|
[\#1010](https://github.com/matrix-org/matrix-js-sdk/pull/1010)
|
||||||
|
* Add API for bulk lookup on the Identity Server
|
||||||
|
[\#1009](https://github.com/matrix-org/matrix-js-sdk/pull/1009)
|
||||||
|
* Remove deprecated authedRequestWithPrefix and requestWithPrefix
|
||||||
|
[\#1000](https://github.com/matrix-org/matrix-js-sdk/pull/1000)
|
||||||
|
* Add API for checking IS account info
|
||||||
|
[\#1007](https://github.com/matrix-org/matrix-js-sdk/pull/1007)
|
||||||
|
* Support rewriting push rules when our internal defaults change
|
||||||
|
[\#1006](https://github.com/matrix-org/matrix-js-sdk/pull/1006)
|
||||||
|
* Upgrade dependencies
|
||||||
|
[\#1005](https://github.com/matrix-org/matrix-js-sdk/pull/1005)
|
||||||
|
|
||||||
|
Changes in [2.3.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.3.2) (2019-09-16)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.3.2-rc.1...v2.3.2)
|
||||||
|
|
||||||
|
* [Release] Fix addPendingEvent with pending event order == chronological
|
||||||
|
[\#1034](https://github.com/matrix-org/matrix-js-sdk/pull/1034)
|
||||||
|
|
||||||
|
Changes in [2.3.2-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.3.2-rc.1) (2019-09-13)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.3.1...v2.3.2-rc.1)
|
||||||
|
|
||||||
|
* Synapse admin functions to release
|
||||||
|
[\#1033](https://github.com/matrix-org/matrix-js-sdk/pull/1033)
|
||||||
|
* [To Release] Add matrix base API to report an event
|
||||||
|
[\#1032](https://github.com/matrix-org/matrix-js-sdk/pull/1032)
|
||||||
|
|
||||||
|
Changes in [2.3.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.3.1) (2019-09-12)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.3.1-rc.1...v2.3.1)
|
||||||
|
|
||||||
|
* No changes since rc.1
|
||||||
|
|
||||||
|
Changes in [2.3.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.3.1-rc.1) (2019-09-11)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.3.0...v2.3.1-rc.1)
|
||||||
|
|
||||||
|
* Update room members on member event redaction
|
||||||
|
[\#1031](https://github.com/matrix-org/matrix-js-sdk/pull/1031)
|
||||||
|
|
||||||
|
Changes in [2.3.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.3.0) (2019-08-05)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.3.0-rc.1...v2.3.0)
|
||||||
|
|
||||||
|
* [release] Support rewriting push rules when our internal defaults change
|
||||||
|
[\#1008](https://github.com/matrix-org/matrix-js-sdk/pull/1008)
|
||||||
|
|
||||||
|
Changes in [2.3.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.3.0-rc.1) (2019-07-31)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.2.0...v2.3.0-rc.1)
|
||||||
|
|
||||||
|
* Add support for IS v2 API with authentication
|
||||||
|
[\#1002](https://github.com/matrix-org/matrix-js-sdk/pull/1002)
|
||||||
|
* Tombstone bugfixes
|
||||||
|
[\#1001](https://github.com/matrix-org/matrix-js-sdk/pull/1001)
|
||||||
|
* Support for MSC2140 (terms of service for IS/IM)
|
||||||
|
[\#988](https://github.com/matrix-org/matrix-js-sdk/pull/988)
|
||||||
|
* Add a request method to /devices
|
||||||
|
[\#994](https://github.com/matrix-org/matrix-js-sdk/pull/994)
|
||||||
|
|
||||||
|
Changes in [2.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.2.0) (2019-07-18)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.2.0-rc.2...v2.2.0)
|
||||||
|
|
||||||
|
* Upgrade lodash dependencies
|
||||||
|
|
||||||
|
Changes in [2.2.0-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.2.0-rc.2) (2019-07-12)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.2.0-rc.1...v2.2.0-rc.2)
|
||||||
|
|
||||||
|
* Fix regression from 2.2.0-rc.1 in request to /devices
|
||||||
|
[\#995](https://github.com/matrix-org/matrix-js-sdk/pull/995)
|
||||||
|
|
||||||
|
Changes in [2.2.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.2.0-rc.1) (2019-07-12)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.1.1...v2.2.0-rc.1)
|
||||||
|
|
||||||
|
* End the verification timer when verification is done
|
||||||
|
[\#993](https://github.com/matrix-org/matrix-js-sdk/pull/993)
|
||||||
|
* Stabilize usage of stably stable APIs (in a stable way)
|
||||||
|
[\#990](https://github.com/matrix-org/matrix-js-sdk/pull/990)
|
||||||
|
* Expose original_event for /relations
|
||||||
|
[\#987](https://github.com/matrix-org/matrix-js-sdk/pull/987)
|
||||||
|
* Process ephemeral events outside timeline handling
|
||||||
|
[\#989](https://github.com/matrix-org/matrix-js-sdk/pull/989)
|
||||||
|
* Don't accept any locally known edits earlier than the last known server-side
|
||||||
|
aggregated edit
|
||||||
|
[\#986](https://github.com/matrix-org/matrix-js-sdk/pull/986)
|
||||||
|
* Get edit date transparently from server aggregations or local echo
|
||||||
|
[\#984](https://github.com/matrix-org/matrix-js-sdk/pull/984)
|
||||||
|
* Add a function to flag keys for backup without scheduling a backup
|
||||||
|
[\#982](https://github.com/matrix-org/matrix-js-sdk/pull/982)
|
||||||
|
* Block read marker and read receipt from advancing into pending events
|
||||||
|
[\#981](https://github.com/matrix-org/matrix-js-sdk/pull/981)
|
||||||
|
* Upgrade dependencies
|
||||||
|
[\#977](https://github.com/matrix-org/matrix-js-sdk/pull/977)
|
||||||
|
* Add default push rule to ignore reactions
|
||||||
|
[\#976](https://github.com/matrix-org/matrix-js-sdk/pull/976)
|
||||||
|
* Fix exception whilst syncing
|
||||||
|
[\#979](https://github.com/matrix-org/matrix-js-sdk/pull/979)
|
||||||
|
* Include the error object when raising Session.logged_out
|
||||||
|
[\#975](https://github.com/matrix-org/matrix-js-sdk/pull/975)
|
||||||
|
|
||||||
|
Changes in [2.1.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.1.1) (2019-07-11)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.1.0...v2.1.1)
|
||||||
|
|
||||||
|
* Process emphemeral events outside timeline handling
|
||||||
|
[\#989](https://github.com/matrix-org/matrix-js-sdk/pull/989)
|
||||||
|
|
||||||
|
Changes in [2.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.1.0) (2019-07-08)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.1.0-rc.1...v2.1.0)
|
||||||
|
|
||||||
|
* Fix exception whilst syncing
|
||||||
|
[\#979](https://github.com/matrix-org/matrix-js-sdk/pull/979)
|
||||||
|
|
||||||
|
Changes in [2.1.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.1.0-rc.1) (2019-07-03)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.0.1...v2.1.0-rc.1)
|
||||||
|
|
||||||
|
* Handle self read receipts for fixing e2e notification counts
|
||||||
|
[\#974](https://github.com/matrix-org/matrix-js-sdk/pull/974)
|
||||||
|
* Add redacts field to event.toJSON
|
||||||
|
[\#973](https://github.com/matrix-org/matrix-js-sdk/pull/973)
|
||||||
|
* Handle associated event send failures
|
||||||
|
[\#972](https://github.com/matrix-org/matrix-js-sdk/pull/972)
|
||||||
|
* Remove irrelevant debug line from timeline handling
|
||||||
|
[\#971](https://github.com/matrix-org/matrix-js-sdk/pull/971)
|
||||||
|
* Handle relations in encrypted rooms
|
||||||
|
[\#969](https://github.com/matrix-org/matrix-js-sdk/pull/969)
|
||||||
|
* Relations endpoint support
|
||||||
|
[\#967](https://github.com/matrix-org/matrix-js-sdk/pull/967)
|
||||||
|
* Disable event encryption for reactions
|
||||||
|
[\#968](https://github.com/matrix-org/matrix-js-sdk/pull/968)
|
||||||
|
* Change the known safe room version to version 4
|
||||||
|
[\#966](https://github.com/matrix-org/matrix-js-sdk/pull/966)
|
||||||
|
* Check for lazy-loading support in the spec versions instead
|
||||||
|
[\#965](https://github.com/matrix-org/matrix-js-sdk/pull/965)
|
||||||
|
* Use camelCase instead of underscore
|
||||||
|
[\#963](https://github.com/matrix-org/matrix-js-sdk/pull/963)
|
||||||
|
* Time out verification attempts after 10 minutes of inactivity
|
||||||
|
[\#961](https://github.com/matrix-org/matrix-js-sdk/pull/961)
|
||||||
|
* Don't handle key verification requests which are immediately cancelled
|
||||||
|
[\#962](https://github.com/matrix-org/matrix-js-sdk/pull/962)
|
||||||
|
|
||||||
|
Changes in [2.0.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.0.1) (2019-06-19)
|
||||||
|
================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.0.1-rc.2...v2.0.1)
|
||||||
|
|
||||||
|
No changes since rc.2
|
||||||
|
|
||||||
|
Changes in [2.0.1-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.0.1-rc.2) (2019-06-18)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.0.1-rc.1...v2.0.1-rc.2)
|
||||||
|
|
||||||
|
* return 'sending' status for an event that is only locally redacted
|
||||||
|
[\#960](https://github.com/matrix-org/matrix-js-sdk/pull/960)
|
||||||
|
* Key verification request fixes
|
||||||
|
[\#954](https://github.com/matrix-org/matrix-js-sdk/pull/954)
|
||||||
|
* Add flag to force saving sync store
|
||||||
|
[\#956](https://github.com/matrix-org/matrix-js-sdk/pull/956)
|
||||||
|
* Expose the inhibit_login flag to register
|
||||||
|
[\#953](https://github.com/matrix-org/matrix-js-sdk/pull/953)
|
||||||
|
* Support redactions and relations of/with unsent events.
|
||||||
|
[\#947](https://github.com/matrix-org/matrix-js-sdk/pull/947)
|
||||||
|
|
||||||
|
Changes in [2.0.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.0.1-rc.1) (2019-06-12)
|
||||||
|
==========================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v2.0.0...v2.0.1-rc.1)
|
||||||
|
|
||||||
|
* Fix content uploads for modern browsers
|
||||||
|
[\#952](https://github.com/matrix-org/matrix-js-sdk/pull/952)
|
||||||
|
* Don't overlap auth submissions with polls
|
||||||
|
[\#951](https://github.com/matrix-org/matrix-js-sdk/pull/951)
|
||||||
|
* Add funding details for GitHub sponsor button
|
||||||
|
[\#945](https://github.com/matrix-org/matrix-js-sdk/pull/945)
|
||||||
|
* Fix backup sig validation with multiple sigs
|
||||||
|
[\#944](https://github.com/matrix-org/matrix-js-sdk/pull/944)
|
||||||
|
* Don't send another token request while one's in flight
|
||||||
|
[\#943](https://github.com/matrix-org/matrix-js-sdk/pull/943)
|
||||||
|
* Don't poll UI auth again until current poll finishes
|
||||||
|
[\#942](https://github.com/matrix-org/matrix-js-sdk/pull/942)
|
||||||
|
* Provide the discovered URLs when a liveliness error occurs
|
||||||
|
[\#938](https://github.com/matrix-org/matrix-js-sdk/pull/938)
|
||||||
|
* Encode event IDs when redacting events
|
||||||
|
[\#941](https://github.com/matrix-org/matrix-js-sdk/pull/941)
|
||||||
|
* add missing logger
|
||||||
|
[\#940](https://github.com/matrix-org/matrix-js-sdk/pull/940)
|
||||||
|
* verification: don't error if we don't know about some keys
|
||||||
|
[\#939](https://github.com/matrix-org/matrix-js-sdk/pull/939)
|
||||||
|
* Local echo for redactions
|
||||||
|
[\#937](https://github.com/matrix-org/matrix-js-sdk/pull/937)
|
||||||
|
* Refresh safe room versions when the server looks more modern than us
|
||||||
|
[\#934](https://github.com/matrix-org/matrix-js-sdk/pull/934)
|
||||||
|
* Add v4 as a safe room version
|
||||||
|
[\#935](https://github.com/matrix-org/matrix-js-sdk/pull/935)
|
||||||
|
* Disable guard-for-in rule
|
||||||
|
[\#933](https://github.com/matrix-org/matrix-js-sdk/pull/933)
|
||||||
|
* Extend loglevel logging for the whole project
|
||||||
|
[\#924](https://github.com/matrix-org/matrix-js-sdk/pull/924)
|
||||||
|
* fix(login): saves access_token and user_id after login for all login types
|
||||||
|
[\#930](https://github.com/matrix-org/matrix-js-sdk/pull/930)
|
||||||
|
* Do not try to request thumbnails with non-integer sizes
|
||||||
|
[\#929](https://github.com/matrix-org/matrix-js-sdk/pull/929)
|
||||||
|
* Revert "Add a bunch of debugging to .well-known IS validation"
|
||||||
|
[\#928](https://github.com/matrix-org/matrix-js-sdk/pull/928)
|
||||||
|
* Add a bunch of debugging to .well-known IS validation
|
||||||
|
[\#927](https://github.com/matrix-org/matrix-js-sdk/pull/927)
|
||||||
|
|
||||||
Changes in [2.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.0.0) (2019-05-31)
|
Changes in [2.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v2.0.0) (2019-05-31)
|
||||||
================================================================================================
|
================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v1.2.0...v2.0.0)
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v1.2.0...v2.0.0)
|
||||||
|
|||||||
@@ -322,13 +322,13 @@ To provide the Olm library in a browser application:
|
|||||||
|
|
||||||
To provide the Olm library in a node.js application:
|
To provide the Olm library in a node.js application:
|
||||||
|
|
||||||
* ``yarn add https://packages.matrix.org/npm/olm/olm-3.0.0.tgz``
|
* ``yarn add https://packages.matrix.org/npm/olm/olm-3.1.4.tgz``
|
||||||
(replace the URL with the latest version you want to use from
|
(replace the URL with the latest version you want to use from
|
||||||
https://packages.matrix.org/npm/olm/)
|
https://packages.matrix.org/npm/olm/)
|
||||||
* ``global.Olm = require('olm');`` *before* loading ``matrix-js-sdk``.
|
* ``global.Olm = require('olm');`` *before* loading ``matrix-js-sdk``.
|
||||||
|
|
||||||
If you want to package Olm as dependency for your node.js application, you can
|
If you want to package Olm as dependency for your node.js application, you can
|
||||||
use ``yarn add https://packages.matrix.org/npm/olm/olm-3.0.0.tgz``. If your
|
use ``yarn add https://packages.matrix.org/npm/olm/olm-3.1.4.tgz``. If your
|
||||||
application also works without e2e crypto enabled, add ``--optional`` to mark it
|
application also works without e2e crypto enabled, add ``--optional`` to mark it
|
||||||
as an optional dependency.
|
as an optional dependency.
|
||||||
|
|
||||||
|
|||||||
22
package.json
22
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "matrix-js-sdk",
|
"name": "matrix-js-sdk",
|
||||||
"version": "2.0.0",
|
"version": "2.4.2",
|
||||||
"description": "Matrix Client-Server SDK for Javascript",
|
"description": "Matrix Client-Server SDK for Javascript",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"build": "babel -s -d lib src && rimraf dist && mkdir dist && browserify -d browser-index.js | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js && terser -c -m -o dist/browser-matrix.min.js --source-map \"content='dist/browser-matrix.js.map'\" dist/browser-matrix.js",
|
"build": "babel -s -d lib src && rimraf dist && mkdir dist && browserify -d browser-index.js | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js && terser -c -m -o dist/browser-matrix.min.js --source-map \"content='dist/browser-matrix.js.map'\" dist/browser-matrix.js",
|
||||||
"dist": "yarn build",
|
"dist": "yarn build",
|
||||||
"watch": "watchify -d browser-index.js -o 'exorcist dist/browser-matrix.js.map > dist/browser-matrix.js' -v",
|
"watch": "watchify -d browser-index.js -o 'exorcist dist/browser-matrix.js.map > dist/browser-matrix.js' -v",
|
||||||
"lint": "eslint --max-warnings 101 src spec",
|
"lint": "eslint --max-warnings 93 src spec",
|
||||||
"prepare": "yarn clean && yarn build && git rev-parse HEAD > git-revision.txt"
|
"prepare": "yarn clean && yarn build && git rev-parse HEAD > git-revision.txt"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -54,11 +54,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"another-json": "^0.2.0",
|
"another-json": "^0.2.0",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"bluebird": "^3.5.0",
|
"bluebird": "3.5.5",
|
||||||
"browser-request": "^0.3.3",
|
"browser-request": "^0.3.3",
|
||||||
"bs58": "^4.0.1",
|
"bs58": "^4.0.1",
|
||||||
"content-type": "^1.0.2",
|
"content-type": "^1.0.2",
|
||||||
"loglevel": "1.6.1",
|
"loglevel": "^1.6.4",
|
||||||
"qs": "^6.5.2",
|
"qs": "^6.5.2",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"unhomoglyph": "^1.0.2"
|
"unhomoglyph": "^1.0.2"
|
||||||
@@ -76,19 +76,19 @@
|
|||||||
"eslint": "^5.12.0",
|
"eslint": "^5.12.0",
|
||||||
"eslint-config-google": "^0.7.1",
|
"eslint-config-google": "^0.7.1",
|
||||||
"eslint-plugin-babel": "^5.3.0",
|
"eslint-plugin-babel": "^5.3.0",
|
||||||
"exorcist": "^0.4.0",
|
"exorcist": "^1.0.1",
|
||||||
"expect": "^1.20.2",
|
"expect": "^1.20.2",
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"jsdoc": "^3.5.5",
|
"jsdoc": "^3.5.5",
|
||||||
"lolex": "^1.5.2",
|
"lolex": "^1.5.2",
|
||||||
"matrix-mock-request": "^1.2.3",
|
"matrix-mock-request": "^1.2.3",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^6.2.1",
|
||||||
"mocha-jenkins-reporter": "^0.4.0",
|
"mocha-jenkins-reporter": "^0.4.0",
|
||||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.0.tgz",
|
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
|
||||||
"rimraf": "^2.5.4",
|
"rimraf": "^3.0.0",
|
||||||
"source-map-support": "^0.4.11",
|
"source-map-support": "^0.5.13",
|
||||||
"sourceify": "^0.1.0",
|
"sourceify": "^1.0.0",
|
||||||
"terser": "^4.0.0",
|
"terser": "^4.3.8",
|
||||||
"watchify": "^3.11.1"
|
"watchify": "^3.11.1"
|
||||||
},
|
},
|
||||||
"browserify": {
|
"browserify": {
|
||||||
|
|||||||
@@ -195,6 +195,11 @@ if [ $dodist -eq 0 ]; then
|
|||||||
pushd "$builddir"
|
pushd "$builddir"
|
||||||
git clone "$projdir" .
|
git clone "$projdir" .
|
||||||
git checkout "$rel_branch"
|
git checkout "$rel_branch"
|
||||||
|
# We use Git branch / commit dependencies for some packages, and Yarn seems
|
||||||
|
# to have a hard time getting that right. See also
|
||||||
|
# https://github.com/yarnpkg/yarn/issues/4734. As a workaround, we clean the
|
||||||
|
# global cache here to ensure we get the right thing.
|
||||||
|
yarn cache clean
|
||||||
yarn install
|
yarn install
|
||||||
# We haven't tagged yet, so tell the dist script what version
|
# We haven't tagged yet, so tell the dist script what version
|
||||||
# it's building
|
# it's building
|
||||||
|
|||||||
@@ -302,11 +302,32 @@ describe("MatrixClient events", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should emit Session.logged_out on M_UNKNOWN_TOKEN", function() {
|
it("should emit Session.logged_out on M_UNKNOWN_TOKEN", function() {
|
||||||
httpBackend.when("GET", "/sync").respond(401, { errcode: 'M_UNKNOWN_TOKEN' });
|
const error = { errcode: 'M_UNKNOWN_TOKEN' };
|
||||||
|
httpBackend.when("GET", "/sync").respond(401, error);
|
||||||
|
|
||||||
let sessionLoggedOutCount = 0;
|
let sessionLoggedOutCount = 0;
|
||||||
client.on("Session.logged_out", function(event, member) {
|
client.on("Session.logged_out", function(errObj) {
|
||||||
sessionLoggedOutCount++;
|
sessionLoggedOutCount++;
|
||||||
|
expect(errObj.data).toEqual(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.startClient();
|
||||||
|
|
||||||
|
return httpBackend.flushAllExpected().then(function() {
|
||||||
|
expect(sessionLoggedOutCount).toEqual(
|
||||||
|
1, "Session.logged_out fired wrong number of times",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should emit Session.logged_out on M_UNKNOWN_TOKEN (soft logout)", function() {
|
||||||
|
const error = { errcode: 'M_UNKNOWN_TOKEN', soft_logout: true };
|
||||||
|
httpBackend.when("GET", "/sync").respond(401, error);
|
||||||
|
|
||||||
|
let sessionLoggedOutCount = 0;
|
||||||
|
client.on("Session.logged_out", function(errObj) {
|
||||||
|
sessionLoggedOutCount++;
|
||||||
|
expect(errObj.data).toEqual(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
client.startClient();
|
client.startClient();
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ describe("MatrixClient", function() {
|
|||||||
const buf = new Buffer('hello world');
|
const buf = new Buffer('hello world');
|
||||||
it("should upload the file", function(done) {
|
it("should upload the file", function(done) {
|
||||||
httpBackend.when(
|
httpBackend.when(
|
||||||
"POST", "/_matrix/media/v1/upload",
|
"POST", "/_matrix/media/r0/upload",
|
||||||
).check(function(req) {
|
).check(function(req) {
|
||||||
expect(req.rawData).toEqual(buf);
|
expect(req.rawData).toEqual(buf);
|
||||||
expect(req.queryParams.filename).toEqual("hi.txt");
|
expect(req.queryParams.filename).toEqual("hi.txt");
|
||||||
@@ -87,7 +87,7 @@ describe("MatrixClient", function() {
|
|||||||
|
|
||||||
it("should parse the response if rawResponse=false", function(done) {
|
it("should parse the response if rawResponse=false", function(done) {
|
||||||
httpBackend.when(
|
httpBackend.when(
|
||||||
"POST", "/_matrix/media/v1/upload",
|
"POST", "/_matrix/media/r0/upload",
|
||||||
).check(function(req) {
|
).check(function(req) {
|
||||||
expect(req.opts.json).toBeFalsy();
|
expect(req.opts.json).toBeFalsy();
|
||||||
}).respond(200, { "content_uri": "uri" });
|
}).respond(200, { "content_uri": "uri" });
|
||||||
@@ -107,7 +107,7 @@ describe("MatrixClient", function() {
|
|||||||
|
|
||||||
it("should parse errors into a MatrixError", function(done) {
|
it("should parse errors into a MatrixError", function(done) {
|
||||||
httpBackend.when(
|
httpBackend.when(
|
||||||
"POST", "/_matrix/media/v1/upload",
|
"POST", "/_matrix/media/r0/upload",
|
||||||
).check(function(req) {
|
).check(function(req) {
|
||||||
expect(req.rawData).toEqual(buf);
|
expect(req.rawData).toEqual(buf);
|
||||||
expect(req.opts.json).toBeFalsy();
|
expect(req.opts.json).toBeFalsy();
|
||||||
@@ -396,7 +396,7 @@ describe("MatrixClient", function() {
|
|||||||
const auth = {a: 1};
|
const auth = {a: 1};
|
||||||
it("should pass through an auth dict", function(done) {
|
it("should pass through an auth dict", function(done) {
|
||||||
httpBackend.when(
|
httpBackend.when(
|
||||||
"DELETE", "/_matrix/client/unstable/devices/my_device",
|
"DELETE", "/_matrix/client/r0/devices/my_device",
|
||||||
).check(function(req) {
|
).check(function(req) {
|
||||||
expect(req.data).toEqual({auth: auth});
|
expect(req.data).toEqual({auth: auth});
|
||||||
}).respond(200);
|
}).respond(200);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ describe("ContentRepo", function() {
|
|||||||
function() {
|
function() {
|
||||||
const mxcUri = "mxc://server.name/resourceid";
|
const mxcUri = "mxc://server.name/resourceid";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
||||||
baseUrl + "/_matrix/media/v1/download/server.name/resourceid",
|
baseUrl + "/_matrix/media/r0/download/server.name/resourceid",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ describe("ContentRepo", function() {
|
|||||||
function() {
|
function() {
|
||||||
const mxcUri = "mxc://server.name/resourceid";
|
const mxcUri = "mxc://server.name/resourceid";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri, 32, 64, "crop")).toEqual(
|
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri, 32, 64, "crop")).toEqual(
|
||||||
baseUrl + "/_matrix/media/v1/thumbnail/server.name/resourceid" +
|
baseUrl + "/_matrix/media/r0/thumbnail/server.name/resourceid" +
|
||||||
"?width=32&height=64&method=crop",
|
"?width=32&height=64&method=crop",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -52,7 +52,7 @@ describe("ContentRepo", function() {
|
|||||||
function() {
|
function() {
|
||||||
const mxcUri = "mxc://server.name/resourceid#automade";
|
const mxcUri = "mxc://server.name/resourceid#automade";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri, 32)).toEqual(
|
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri, 32)).toEqual(
|
||||||
baseUrl + "/_matrix/media/v1/thumbnail/server.name/resourceid" +
|
baseUrl + "/_matrix/media/r0/thumbnail/server.name/resourceid" +
|
||||||
"?width=32#automade",
|
"?width=32#automade",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -61,7 +61,7 @@ describe("ContentRepo", function() {
|
|||||||
function() {
|
function() {
|
||||||
const mxcUri = "mxc://server.name/resourceid#automade";
|
const mxcUri = "mxc://server.name/resourceid#automade";
|
||||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
||||||
baseUrl + "/_matrix/media/v1/download/server.name/resourceid#automade",
|
baseUrl + "/_matrix/media/r0/download/server.name/resourceid#automade",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -73,21 +73,21 @@ describe("ContentRepo", function() {
|
|||||||
|
|
||||||
it("should set w/h by default to 96", function() {
|
it("should set w/h by default to 96", function() {
|
||||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foobar")).toEqual(
|
expect(ContentRepo.getIdenticonUri(baseUrl, "foobar")).toEqual(
|
||||||
baseUrl + "/_matrix/media/v1/identicon/foobar" +
|
baseUrl + "/_matrix/media/unstable/identicon/foobar" +
|
||||||
"?width=96&height=96",
|
"?width=96&height=96",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be able to set custom w/h", function() {
|
it("should be able to set custom w/h", function() {
|
||||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foobar", 32, 64)).toEqual(
|
expect(ContentRepo.getIdenticonUri(baseUrl, "foobar", 32, 64)).toEqual(
|
||||||
baseUrl + "/_matrix/media/v1/identicon/foobar" +
|
baseUrl + "/_matrix/media/unstable/identicon/foobar" +
|
||||||
"?width=32&height=64",
|
"?width=32&height=64",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should URL encode the identicon string", function() {
|
it("should URL encode the identicon string", function() {
|
||||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foo#bar", 32, 64)).toEqual(
|
expect(ContentRepo.getIdenticonUri(baseUrl, "foo#bar", 32, 64)).toEqual(
|
||||||
baseUrl + "/_matrix/media/v1/identicon/foo%23bar" +
|
baseUrl + "/_matrix/media/unstable/identicon/foo%23bar" +
|
||||||
"?width=32&height=64",
|
"?width=32&height=64",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,8 +69,14 @@ describe("verification request", function() {
|
|||||||
bob.client.on("crypto.verification.request", (request) => {
|
bob.client.on("crypto.verification.request", (request) => {
|
||||||
const bobVerifier = request.beginKeyVerification(verificationMethods.SAS);
|
const bobVerifier = request.beginKeyVerification(verificationMethods.SAS);
|
||||||
bobVerifier.verify();
|
bobVerifier.verify();
|
||||||
|
|
||||||
|
// XXX: Private function access (but it's a test, so we're okay)
|
||||||
|
bobVerifier._endTimer();
|
||||||
});
|
});
|
||||||
const aliceVerifier = await alice.client.requestVerification("@bob:example.com");
|
const aliceVerifier = await alice.client.requestVerification("@bob:example.com");
|
||||||
expect(aliceVerifier).toBeAn(SAS);
|
expect(aliceVerifier).toBeAn(SAS);
|
||||||
|
|
||||||
|
// XXX: Private function access (but it's a test, so we're okay)
|
||||||
|
aliceVerifier._endTimer();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ describe("SAS verification", function() {
|
|||||||
await sas.verify()
|
await sas.verify()
|
||||||
.catch(spy);
|
.catch(spy);
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Cancel the SAS for cleanup (we started a verification, so abort)
|
||||||
|
sas.cancel();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("verification", function() {
|
describe("verification", function() {
|
||||||
@@ -370,4 +373,127 @@ describe("SAS verification", function() {
|
|||||||
expect(bob.client.setDeviceVerified)
|
expect(bob.client.setDeviceVerified)
|
||||||
.toNotHaveBeenCalled();
|
.toNotHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("verification in DM", function() {
|
||||||
|
let alice;
|
||||||
|
let bob;
|
||||||
|
let aliceSasEvent;
|
||||||
|
let bobSasEvent;
|
||||||
|
let aliceVerifier;
|
||||||
|
let bobPromise;
|
||||||
|
|
||||||
|
beforeEach(async function() {
|
||||||
|
[alice, bob] = await makeTestClients(
|
||||||
|
[
|
||||||
|
{userId: "@alice:example.com", deviceId: "Osborne2"},
|
||||||
|
{userId: "@bob:example.com", deviceId: "Dynabook"},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
verificationMethods: [verificationMethods.SAS],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
alice.setDeviceVerified = expect.createSpy();
|
||||||
|
alice.getDeviceEd25519Key = () => {
|
||||||
|
return "alice+base64+ed25519+key";
|
||||||
|
};
|
||||||
|
alice.getStoredDevice = () => {
|
||||||
|
return DeviceInfo.fromStorage(
|
||||||
|
{
|
||||||
|
keys: {
|
||||||
|
"ed25519:Dynabook": "bob+base64+ed25519+key",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Dynabook",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
alice.downloadKeys = () => {
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
bob.setDeviceVerified = expect.createSpy();
|
||||||
|
bob.getStoredDevice = () => {
|
||||||
|
return DeviceInfo.fromStorage(
|
||||||
|
{
|
||||||
|
keys: {
|
||||||
|
"ed25519:Osborne2": "alice+base64+ed25519+key",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Osborne2",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
bob.getDeviceEd25519Key = () => {
|
||||||
|
return "bob+base64+ed25519+key";
|
||||||
|
};
|
||||||
|
bob.downloadKeys = () => {
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
aliceSasEvent = null;
|
||||||
|
bobSasEvent = null;
|
||||||
|
|
||||||
|
bobPromise = new Promise((resolve, reject) => {
|
||||||
|
bob.on("event", async (event) => {
|
||||||
|
const content = event.getContent();
|
||||||
|
if (event.getType() === "m.room.message"
|
||||||
|
&& content.msgtype === "m.key.verification.request") {
|
||||||
|
expect(content.methods).toInclude(SAS.NAME);
|
||||||
|
expect(content.to).toBe(bob.getUserId());
|
||||||
|
const verifier = bob.acceptVerificationDM(event, SAS.NAME);
|
||||||
|
verifier.on("show_sas", (e) => {
|
||||||
|
if (!e.sas.emoji || !e.sas.decimal) {
|
||||||
|
e.cancel();
|
||||||
|
} else if (!aliceSasEvent) {
|
||||||
|
bobSasEvent = e;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
expect(e.sas).toEqual(aliceSasEvent.sas);
|
||||||
|
e.confirm();
|
||||||
|
aliceSasEvent.confirm();
|
||||||
|
} catch (error) {
|
||||||
|
e.mismatch();
|
||||||
|
aliceSasEvent.mismatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await verifier.verify();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
aliceVerifier = await alice.requestVerificationDM(
|
||||||
|
bob.getUserId(), "!room_id", [verificationMethods.SAS],
|
||||||
|
);
|
||||||
|
aliceVerifier.on("show_sas", (e) => {
|
||||||
|
if (!e.sas.emoji || !e.sas.decimal) {
|
||||||
|
e.cancel();
|
||||||
|
} else if (!bobSasEvent) {
|
||||||
|
aliceSasEvent = e;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
expect(e.sas).toEqual(bobSasEvent.sas);
|
||||||
|
e.confirm();
|
||||||
|
bobSasEvent.confirm();
|
||||||
|
} catch (error) {
|
||||||
|
e.mismatch();
|
||||||
|
bobSasEvent.mismatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should verify a key", async function() {
|
||||||
|
await Promise.all([
|
||||||
|
aliceVerifier.verify(),
|
||||||
|
bobPromise,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// make sure Alice and Bob verified each other
|
||||||
|
expect(alice.setDeviceVerified)
|
||||||
|
.toHaveBeenCalledWith(bob.getUserId(), bob.deviceId);
|
||||||
|
expect(bob.setDeviceVerified)
|
||||||
|
.toHaveBeenCalledWith(alice.getUserId(), alice.deviceId);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,6 +48,25 @@ export async function makeTestClients(userInfos, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const sendEvent = function(room, type, content) {
|
||||||
|
// make up a unique ID as the event ID
|
||||||
|
const eventId = "$" + this.makeTxnId(); // eslint-disable-line babel/no-invalid-this
|
||||||
|
const event = new MatrixEvent({
|
||||||
|
sender: this.getUserId(), // eslint-disable-line babel/no-invalid-this
|
||||||
|
type: type,
|
||||||
|
content: content,
|
||||||
|
room_id: room,
|
||||||
|
event_id: eventId,
|
||||||
|
});
|
||||||
|
for (const client of clients) {
|
||||||
|
setTimeout(
|
||||||
|
() => client.emit("event", event),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {event_id: eventId};
|
||||||
|
};
|
||||||
|
|
||||||
for (const userInfo of userInfos) {
|
for (const userInfo of userInfos) {
|
||||||
const testClient = new TestClient(
|
const testClient = new TestClient(
|
||||||
@@ -59,6 +78,7 @@ export async function makeTestClients(userInfos, options) {
|
|||||||
}
|
}
|
||||||
clientMap[userInfo.userId][userInfo.deviceId] = testClient.client;
|
clientMap[userInfo.userId][userInfo.deviceId] = testClient.client;
|
||||||
testClient.client.sendToDevice = sendToDevice;
|
testClient.client.sendToDevice = sendToDevice;
|
||||||
|
testClient.client.sendEvent = sendEvent;
|
||||||
clients.push(testClient);
|
clients.push(testClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,12 +154,9 @@ describe("MatrixClient", function() {
|
|||||||
});
|
});
|
||||||
// FIXME: We shouldn't be yanking _http like this.
|
// FIXME: We shouldn't be yanking _http like this.
|
||||||
client._http = [
|
client._http = [
|
||||||
"authedRequest", "authedRequestWithPrefix", "getContentUri",
|
"authedRequest", "getContentUri", "request", "uploadContent",
|
||||||
"request", "requestWithPrefix", "uploadContent",
|
|
||||||
].reduce((r, k) => { r[k] = expect.createSpy(); return r; }, {});
|
].reduce((r, k) => { r[k] = expect.createSpy(); return r; }, {});
|
||||||
client._http.authedRequest.andCall(httpReq);
|
client._http.authedRequest.andCall(httpReq);
|
||||||
client._http.authedRequestWithPrefix.andCall(httpReq);
|
|
||||||
client._http.requestWithPrefix.andCall(httpReq);
|
|
||||||
client._http.request.andCall(httpReq);
|
client._http.request.andCall(httpReq);
|
||||||
|
|
||||||
// set reasonable working defaults
|
// set reasonable working defaults
|
||||||
@@ -181,9 +178,6 @@ describe("MatrixClient", function() {
|
|||||||
client._http.authedRequest.andCall(function() {
|
client._http.authedRequest.andCall(function() {
|
||||||
return Promise.defer().promise;
|
return Promise.defer().promise;
|
||||||
});
|
});
|
||||||
client._http.authedRequestWithPrefix.andCall(function() {
|
|
||||||
return Promise.defer().promise;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not POST /filter if a matching filter already exists", async function() {
|
it("should not POST /filter if a matching filter already exists", async function() {
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ describe("Room", function() {
|
|||||||
user_ids: [userA],
|
user_ids: [userA],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
room.addLiveEvents([typing]);
|
room.addEphemeralEvents([typing]);
|
||||||
expect(room.currentState.setTypingEvent).toHaveBeenCalledWith(typing);
|
expect(room.currentState.setTypingEvent).toHaveBeenCalledWith(typing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -34,12 +34,18 @@ export default class Reemitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reEmit(source, eventNames) {
|
reEmit(source, eventNames) {
|
||||||
|
// We include the source as the last argument for event handlers which may need it,
|
||||||
|
// such as read receipt listeners on the client class which won't have the context
|
||||||
|
// of the room.
|
||||||
|
const forSource = (handler, ...args) => {
|
||||||
|
handler(...args, source);
|
||||||
|
};
|
||||||
for (const eventName of eventNames) {
|
for (const eventName of eventNames) {
|
||||||
if (this.boundHandlers[eventName] === undefined) {
|
if (this.boundHandlers[eventName] === undefined) {
|
||||||
this.boundHandlers[eventName] = this._handleEvent.bind(this, eventName);
|
this.boundHandlers[eventName] = this._handleEvent.bind(this, eventName);
|
||||||
}
|
}
|
||||||
const boundHandler = this.boundHandlers[eventName];
|
|
||||||
|
|
||||||
|
const boundHandler = forSource.bind(this, this.boundHandlers[eventName]);
|
||||||
source.on(eventName, boundHandler);
|
source.on(eventName, boundHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -429,6 +429,26 @@ export class AutoDiscovery {
|
|||||||
return AutoDiscovery.fromDiscoveryConfig(wellknown.raw);
|
return AutoDiscovery.fromDiscoveryConfig(wellknown.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the raw discovery client configuration for the given domain name.
|
||||||
|
* Should only be used if there's no validation to be done on the resulting
|
||||||
|
* object, otherwise use findClientConfig().
|
||||||
|
* @param {string} domain The domain to get the client config for.
|
||||||
|
* @returns {Promise<object>} Resolves to the domain's client config. Can
|
||||||
|
* be an empty object.
|
||||||
|
*/
|
||||||
|
static async getRawClientConfig(domain) {
|
||||||
|
if (!domain || typeof(domain) !== "string" || domain.length === 0) {
|
||||||
|
throw new Error("'domain' must be a string of non-zero length");
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this._fetchWellKnownObject(
|
||||||
|
`https://${domain}/.well-known/matrix/client`,
|
||||||
|
);
|
||||||
|
if (!response) return {};
|
||||||
|
return response.raw || {};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitizes a given URL to ensure it is either an HTTP or HTTP URL and
|
* Sanitizes a given URL to ensure it is either an HTTP or HTTP URL and
|
||||||
* is suitable for the requirements laid out by .well-known auto discovery.
|
* is suitable for the requirements laid out by .well-known auto discovery.
|
||||||
|
|||||||
684
src/base-apis.js
684
src/base-apis.js
@@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -23,8 +25,23 @@ limitations under the License.
|
|||||||
* @module base-apis
|
* @module base-apis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { SERVICE_TYPES } from './service-types';
|
||||||
|
import logger from './logger';
|
||||||
|
|
||||||
const httpApi = require("./http-api");
|
const httpApi = require("./http-api");
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
|
const PushProcessor = require("./pushprocessor");
|
||||||
|
|
||||||
|
function termsUrlForService(serviceType, baseUrl) {
|
||||||
|
switch (serviceType) {
|
||||||
|
case SERVICE_TYPES.IS:
|
||||||
|
return baseUrl + httpApi.PREFIX_IDENTITY_V2 + '/terms';
|
||||||
|
case SERVICE_TYPES.IM:
|
||||||
|
return baseUrl + '/_matrix/integrations/v1/terms';
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported service type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Low-level wrappers for the Matrix APIs
|
* Low-level wrappers for the Matrix APIs
|
||||||
@@ -46,6 +63,15 @@ const utils = require("./utils");
|
|||||||
*
|
*
|
||||||
* @param {string} opts.accessToken The access_token for this user.
|
* @param {string} opts.accessToken The access_token for this user.
|
||||||
*
|
*
|
||||||
|
* @param {IdentityServerProvider} [opts.identityServer]
|
||||||
|
* Optional. A provider object with one function `getAccessToken`, which is a
|
||||||
|
* callback that returns a Promise<String> of an identity access token to supply
|
||||||
|
* with identity requests. If the object is unset, no access token will be
|
||||||
|
* supplied.
|
||||||
|
* See also https://github.com/vector-im/riot-web/issues/10615 which seeks to
|
||||||
|
* replace the previous approach of manual access tokens params with this
|
||||||
|
* callback throughout the SDK.
|
||||||
|
*
|
||||||
* @param {Number=} opts.localTimeoutMs Optional. The default maximum amount of
|
* @param {Number=} opts.localTimeoutMs Optional. The default maximum amount of
|
||||||
* time to wait before timing out HTTP requests. If not specified, there is no
|
* time to wait before timing out HTTP requests. If not specified, there is no
|
||||||
* timeout.
|
* timeout.
|
||||||
@@ -62,6 +88,7 @@ function MatrixBaseApis(opts) {
|
|||||||
|
|
||||||
this.baseUrl = opts.baseUrl;
|
this.baseUrl = opts.baseUrl;
|
||||||
this.idBaseUrl = opts.idBaseUrl;
|
this.idBaseUrl = opts.idBaseUrl;
|
||||||
|
this.identityServer = opts.identityServer;
|
||||||
|
|
||||||
const httpOpts = {
|
const httpOpts = {
|
||||||
baseUrl: opts.baseUrl,
|
baseUrl: opts.baseUrl,
|
||||||
@@ -100,6 +127,15 @@ MatrixBaseApis.prototype.getIdentityServerUrl = function(stripProto=false) {
|
|||||||
return this.idBaseUrl;
|
return this.idBaseUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Identity Server URL of this client
|
||||||
|
* @param {string} url New Identity Server URL
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.setIdentityServerUrl = function(url) {
|
||||||
|
this.idBaseUrl = utils.ensureNoTrailingSlash(url);
|
||||||
|
this._http.setIdBaseUrl(this.idBaseUrl);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the access token associated with this account.
|
* Get the access token associated with this account.
|
||||||
* @return {?String} The access_token or null
|
* @return {?String} The access_token or null
|
||||||
@@ -391,9 +427,8 @@ MatrixBaseApis.prototype.deactivateAccount = function(auth, erase) {
|
|||||||
body.erase = erase;
|
body.erase = erase;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(
|
||||||
undefined, "POST", '/account/deactivate', undefined, body,
|
undefined, "POST", '/account/deactivate', undefined, body,
|
||||||
httpApi.PREFIX_R0,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -438,6 +473,37 @@ MatrixBaseApis.prototype.createRoom = function(options, callback) {
|
|||||||
callback, "POST", "/createRoom", undefined, options,
|
callback, "POST", "/createRoom", undefined, options,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Fetches relations for a given event
|
||||||
|
* @param {string} roomId the room of the event
|
||||||
|
* @param {string} eventId the id of the event
|
||||||
|
* @param {string} relationType the rel_type of the relations requested
|
||||||
|
* @param {string} eventType the event type of the relations requested
|
||||||
|
* @param {Object} opts options with optional values for the request.
|
||||||
|
* @param {Object} opts.from the pagination token returned from a previous request as `next_batch` to return following relations.
|
||||||
|
* @return {Object} the response, with chunk and next_batch.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.fetchRelations =
|
||||||
|
async function(roomId, eventId, relationType, eventType, opts) {
|
||||||
|
const queryParams = {};
|
||||||
|
if (opts.from) {
|
||||||
|
queryParams.from = opts.from;
|
||||||
|
}
|
||||||
|
const queryString = utils.encodeParams(queryParams);
|
||||||
|
const path = utils.encodeUri(
|
||||||
|
"/rooms/$roomId/relations/$eventId/$relationType/$eventType?" + queryString, {
|
||||||
|
$roomId: roomId,
|
||||||
|
$eventId: eventId,
|
||||||
|
$relationType: relationType,
|
||||||
|
$eventType: eventType,
|
||||||
|
});
|
||||||
|
const response = await this._http.authedRequest(
|
||||||
|
undefined, "GET", path, null, null, {
|
||||||
|
prefix: httpApi.PREFIX_UNSTABLE,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} roomId
|
* @param {string} roomId
|
||||||
@@ -927,10 +993,13 @@ MatrixBaseApis.prototype.roomInitialSync = function(roomId, limit, callback) {
|
|||||||
* @param {string} rrEventId ID of the event tracked by the read receipt. This is here
|
* @param {string} rrEventId ID of the event tracked by the read receipt. This is here
|
||||||
* for convenience because the RR and the RM are commonly updated at the same time as
|
* for convenience because the RR and the RM are commonly updated at the same time as
|
||||||
* each other. Optional.
|
* each other. Optional.
|
||||||
|
* @param {object} opts Options for the read markers.
|
||||||
|
* @param {object} opts.hidden True to hide the read receipt from other users. <b>This
|
||||||
|
* property is currently unstable and may change in the future.</b>
|
||||||
* @return {module:client.Promise} Resolves: the empty object, {}.
|
* @return {module:client.Promise} Resolves: the empty object, {}.
|
||||||
*/
|
*/
|
||||||
MatrixBaseApis.prototype.setRoomReadMarkersHttpRequest =
|
MatrixBaseApis.prototype.setRoomReadMarkersHttpRequest =
|
||||||
function(roomId, rmEventId, rrEventId) {
|
function(roomId, rmEventId, rrEventId, opts) {
|
||||||
const path = utils.encodeUri("/rooms/$roomId/read_markers", {
|
const path = utils.encodeUri("/rooms/$roomId/read_markers", {
|
||||||
$roomId: roomId,
|
$roomId: roomId,
|
||||||
});
|
});
|
||||||
@@ -938,6 +1007,7 @@ MatrixBaseApis.prototype.setRoomReadMarkersHttpRequest =
|
|||||||
const content = {
|
const content = {
|
||||||
"m.fully_read": rmEventId,
|
"m.fully_read": rmEventId,
|
||||||
"m.read": rrEventId,
|
"m.read": rrEventId,
|
||||||
|
"m.hidden": Boolean(opts ? opts.hidden : false),
|
||||||
};
|
};
|
||||||
|
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
@@ -1269,10 +1339,16 @@ MatrixBaseApis.prototype.getThreePids = function(callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Add a 3PID to your homeserver account and optionally bind it to an identity
|
||||||
|
* server as well. An identity server is required as part of the `creds` object.
|
||||||
|
*
|
||||||
|
* This API is deprecated, and you should instead use `addThreePidOnly`
|
||||||
|
* for homeservers that support it.
|
||||||
|
*
|
||||||
* @param {Object} creds
|
* @param {Object} creds
|
||||||
* @param {boolean} bind
|
* @param {boolean} bind
|
||||||
* @param {module:client.callback} callback Optional.
|
* @param {module:client.callback} callback Optional.
|
||||||
* @return {module:client.Promise} Resolves: TODO
|
* @return {module:client.Promise} Resolves: on success
|
||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
*/
|
*/
|
||||||
MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) {
|
MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) {
|
||||||
@@ -1286,6 +1362,75 @@ MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a 3PID to your homeserver account. This API does not use an identity
|
||||||
|
* server, as the homeserver is expected to handle 3PID ownership validation.
|
||||||
|
*
|
||||||
|
* You can check whether a homeserver supports this API via
|
||||||
|
* `doesServerSupportSeparateAddAndBind`.
|
||||||
|
*
|
||||||
|
* @param {Object} data A object with 3PID validation data from having called
|
||||||
|
* `account/3pid/<medium>/requestToken` on the homeserver.
|
||||||
|
* @return {module:client.Promise} Resolves: on success
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.addThreePidOnly = function(data) {
|
||||||
|
const path = "/account/3pid/add";
|
||||||
|
return this._http.authedRequest(
|
||||||
|
undefined, "POST", path, null, data, {
|
||||||
|
prefix: httpApi.PREFIX_UNSTABLE,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind a 3PID for discovery onto an identity server via the homeserver. The
|
||||||
|
* identity server handles 3PID ownership validation and the homeserver records
|
||||||
|
* the new binding to track where all 3PIDs for the account are bound.
|
||||||
|
*
|
||||||
|
* You can check whether a homeserver supports this API via
|
||||||
|
* `doesServerSupportSeparateAddAndBind`.
|
||||||
|
*
|
||||||
|
* @param {Object} data A object with 3PID validation data from having called
|
||||||
|
* `validate/<medium>/requestToken` on the identity server. It should also
|
||||||
|
* contain `id_server` and `id_access_token` fields as well.
|
||||||
|
* @return {module:client.Promise} Resolves: on success
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.bindThreePid = function(data) {
|
||||||
|
const path = "/account/3pid/bind";
|
||||||
|
return this._http.authedRequest(
|
||||||
|
undefined, "POST", path, null, data, {
|
||||||
|
prefix: httpApi.PREFIX_UNSTABLE,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbind a 3PID for discovery on an identity server via the homeserver. The
|
||||||
|
* homeserver removes its record of the binding to keep an updated record of
|
||||||
|
* where all 3PIDs for the account are bound.
|
||||||
|
*
|
||||||
|
* @param {string} medium The threepid medium (eg. 'email')
|
||||||
|
* @param {string} address The threepid address (eg. 'bob@example.com')
|
||||||
|
* this must be as returned by getThreePids.
|
||||||
|
* @return {module:client.Promise} Resolves: on success
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.unbindThreePid = function(medium, address) {
|
||||||
|
const path = "/account/3pid/unbind";
|
||||||
|
const data = {
|
||||||
|
medium,
|
||||||
|
address,
|
||||||
|
id_server: this.getIdentityServerUrl(true),
|
||||||
|
};
|
||||||
|
return this._http.authedRequest(
|
||||||
|
undefined, "POST", path, null, data, {
|
||||||
|
prefix: httpApi.PREFIX_UNSTABLE,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} medium The threepid medium (eg. 'email')
|
* @param {string} medium The threepid medium (eg. 'email')
|
||||||
* @param {string} address The threepid address (eg. 'bob@example.com')
|
* @param {string} address The threepid address (eg. 'bob@example.com')
|
||||||
@@ -1300,9 +1445,7 @@ MatrixBaseApis.prototype.deleteThreePid = function(medium, address) {
|
|||||||
'medium': medium,
|
'medium': medium,
|
||||||
'address': address,
|
'address': address,
|
||||||
};
|
};
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(undefined, "POST", path, null, data);
|
||||||
undefined, "POST", path, null, data, httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1335,10 +1478,8 @@ MatrixBaseApis.prototype.setPassword = function(authDict, newPassword, callback)
|
|||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
*/
|
*/
|
||||||
MatrixBaseApis.prototype.getDevices = function() {
|
MatrixBaseApis.prototype.getDevices = function() {
|
||||||
const path = "/devices";
|
return this._http.authedRequest(
|
||||||
return this._http.authedRequestWithPrefix(
|
undefined, 'GET', "/devices", undefined, undefined,
|
||||||
undefined, "GET", path, undefined, undefined,
|
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1355,11 +1496,7 @@ MatrixBaseApis.prototype.setDeviceDetails = function(device_id, body) {
|
|||||||
$device_id: device_id,
|
$device_id: device_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this._http.authedRequest(undefined, "PUT", path, undefined, body);
|
||||||
return this._http.authedRequestWithPrefix(
|
|
||||||
undefined, "PUT", path, undefined, body,
|
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1381,10 +1518,7 @@ MatrixBaseApis.prototype.deleteDevice = function(device_id, auth) {
|
|||||||
body.auth = auth;
|
body.auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(undefined, "DELETE", path, undefined, body);
|
||||||
undefined, "DELETE", path, undefined, body,
|
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1402,10 +1536,8 @@ MatrixBaseApis.prototype.deleteMultipleDevices = function(devices, auth) {
|
|||||||
body.auth = auth;
|
body.auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._http.authedRequestWithPrefix(
|
const path = "/delete_devices";
|
||||||
undefined, "POST", "/delete_devices", undefined, body,
|
return this._http.authedRequest(undefined, "POST", path, undefined, body);
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1447,7 +1579,9 @@ MatrixBaseApis.prototype.setPusher = function(pusher, callback) {
|
|||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
*/
|
*/
|
||||||
MatrixBaseApis.prototype.getPushRules = function(callback) {
|
MatrixBaseApis.prototype.getPushRules = function(callback) {
|
||||||
return this._http.authedRequest(callback, "GET", "/pushrules/");
|
return this._http.authedRequest(callback, "GET", "/pushrules/").then(rules => {
|
||||||
|
return PushProcessor.rewriteDefaultRules(rules);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1581,9 +1715,7 @@ MatrixBaseApis.prototype.uploadKeysRequest = function(content, opts, callback) {
|
|||||||
} else {
|
} else {
|
||||||
path = "/keys/upload";
|
path = "/keys/upload";
|
||||||
}
|
}
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(callback, "POST", path, undefined, content);
|
||||||
callback, "POST", path, undefined, content, httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MatrixBaseApis.prototype.uploadKeySignatures = function(content) {
|
MatrixBaseApis.prototype.uploadKeySignatures = function(content) {
|
||||||
@@ -1625,10 +1757,7 @@ MatrixBaseApis.prototype.downloadKeysForUsers = function(userIds, opts) {
|
|||||||
content.device_keys[u] = {};
|
content.device_keys[u] = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(undefined, "POST", "/keys/query", undefined, content);
|
||||||
undefined, "POST", "/keys/query", undefined, content,
|
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1656,10 +1785,8 @@ MatrixBaseApis.prototype.claimOneTimeKeys = function(devices, key_algorithm) {
|
|||||||
query[deviceId] = key_algorithm;
|
query[deviceId] = key_algorithm;
|
||||||
}
|
}
|
||||||
const content = {one_time_keys: queries};
|
const content = {one_time_keys: queries};
|
||||||
return this._http.authedRequestWithPrefix(
|
const path = "/keys/claim";
|
||||||
undefined, "POST", "/keys/claim", undefined, content,
|
return this._http.authedRequest(undefined, "POST", path, undefined, content);
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1678,10 +1805,8 @@ MatrixBaseApis.prototype.getKeyChanges = function(oldToken, newToken) {
|
|||||||
to: newToken,
|
to: newToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
return this._http.authedRequestWithPrefix(
|
const path = "/keys/changes";
|
||||||
undefined, "GET", "/keys/changes", qps, undefined,
|
return this._http.authedRequest(undefined, "GET", path, qps, undefined);
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MatrixBaseApis.prototype.uploadDeviceSigningKeys = function(auth, keys) {
|
MatrixBaseApis.prototype.uploadDeviceSigningKeys = function(auth, keys) {
|
||||||
@@ -1696,10 +1821,36 @@ MatrixBaseApis.prototype.uploadDeviceSigningKeys = function(auth, keys) {
|
|||||||
// ==========================
|
// ==========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests an email verification token directly from an Identity Server.
|
* Register with an Identity Server using the OpenID token from the user's
|
||||||
|
* Homeserver, which can be retrieved via
|
||||||
|
* {@link module:client~MatrixClient#getOpenIdToken}.
|
||||||
*
|
*
|
||||||
* Note that the Home Server offers APIs to proxy this API for specific
|
* Note that the `/account/register` endpoint (as well as IS authentication in
|
||||||
* situations, allowing for better feedback to the user.
|
* general) was added as part of the v2 API version.
|
||||||
|
*
|
||||||
|
* @param {object} hsOpenIdToken
|
||||||
|
* @return {module:client.Promise} Resolves: with object containing an Identity
|
||||||
|
* Server access token.
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.registerWithIdentityServer = function(hsOpenIdToken) {
|
||||||
|
if (!this.idBaseUrl) {
|
||||||
|
throw new Error("No Identity Server base URL set");
|
||||||
|
}
|
||||||
|
|
||||||
|
const uri = this.idBaseUrl + httpApi.PREFIX_IDENTITY_V2 + "/account/register";
|
||||||
|
return this._http.requestOtherUrl(
|
||||||
|
undefined, "POST", uri,
|
||||||
|
null, hsOpenIdToken,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests an email verification token directly from an identity server.
|
||||||
|
*
|
||||||
|
* This API is used as part of binding an email for discovery on an identity
|
||||||
|
* server. The validation data that results should be passed to the
|
||||||
|
* `bindThreePid` method to complete the binding process.
|
||||||
*
|
*
|
||||||
* @param {string} email The email address to request a token for
|
* @param {string} email The email address to request a token for
|
||||||
* @param {string} clientSecret A secret binary string generated by the client.
|
* @param {string} clientSecret A secret binary string generated by the client.
|
||||||
@@ -1711,26 +1862,122 @@ MatrixBaseApis.prototype.uploadDeviceSigningKeys = function(auth, keys) {
|
|||||||
* @param {string} nextLink Optional If specified, the client will be redirected
|
* @param {string} nextLink Optional If specified, the client will be redirected
|
||||||
* to this link after validation.
|
* to this link after validation.
|
||||||
* @param {module:client.callback} callback Optional.
|
* @param {module:client.callback} callback Optional.
|
||||||
|
* @param {string} identityAccessToken The `access_token` field of the identity
|
||||||
|
* server `/account/register` response (see {@link registerWithIdentityServer}).
|
||||||
|
*
|
||||||
* @return {module:client.Promise} Resolves: TODO
|
* @return {module:client.Promise} Resolves: TODO
|
||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
* @throws Error if No ID server is set
|
* @throws Error if no identity server is set
|
||||||
*/
|
*/
|
||||||
MatrixBaseApis.prototype.requestEmailToken = function(email, clientSecret,
|
MatrixBaseApis.prototype.requestEmailToken = async function(
|
||||||
sendAttempt, nextLink, callback) {
|
email,
|
||||||
|
clientSecret,
|
||||||
|
sendAttempt,
|
||||||
|
nextLink,
|
||||||
|
callback,
|
||||||
|
identityAccessToken,
|
||||||
|
) {
|
||||||
const params = {
|
const params = {
|
||||||
client_secret: clientSecret,
|
client_secret: clientSecret,
|
||||||
email: email,
|
email: email,
|
||||||
send_attempt: sendAttempt,
|
send_attempt: sendAttempt,
|
||||||
next_link: nextLink,
|
next_link: nextLink,
|
||||||
};
|
};
|
||||||
return this._http.idServerRequest(
|
|
||||||
|
try {
|
||||||
|
const response = await this._http.idServerRequest(
|
||||||
|
undefined, "POST", "/validate/email/requestToken",
|
||||||
|
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
|
);
|
||||||
|
// TODO: Fold callback into above call once v1 path below is removed
|
||||||
|
if (callback) callback(null, response);
|
||||||
|
return response;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.cors === "rejected" || err.httpStatus === 404) {
|
||||||
|
// Fall back to deprecated v1 API for now
|
||||||
|
// TODO: Remove this path once v2 is only supported version
|
||||||
|
// See https://github.com/vector-im/riot-web/issues/10443
|
||||||
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
|
return await this._http.idServerRequest(
|
||||||
callback, "POST", "/validate/email/requestToken",
|
callback, "POST", "/validate/email/requestToken",
|
||||||
params, httpApi.PREFIX_IDENTITY_V1,
|
params, httpApi.PREFIX_IDENTITY_V1,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (callback) callback(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submits an MSISDN token to the identity server
|
* Requests a MSISDN verification token directly from an identity server.
|
||||||
|
*
|
||||||
|
* This API is used as part of binding a MSISDN for discovery on an identity
|
||||||
|
* server. The validation data that results should be passed to the
|
||||||
|
* `bindThreePid` method to complete the binding process.
|
||||||
|
*
|
||||||
|
* @param {string} phoneCountry The ISO 3166-1 alpha-2 code for the country in
|
||||||
|
* which phoneNumber should be parsed relative to.
|
||||||
|
* @param {string} phoneNumber The phone number, in national or international
|
||||||
|
* format
|
||||||
|
* @param {string} clientSecret A secret binary string generated by the client.
|
||||||
|
* It is recommended this be around 16 ASCII characters.
|
||||||
|
* @param {number} sendAttempt If an identity server sees a duplicate request
|
||||||
|
* with the same sendAttempt, it will not send another SMS.
|
||||||
|
* To request another SMS to be sent, use a larger value for
|
||||||
|
* the sendAttempt param as was used in the previous request.
|
||||||
|
* @param {string} nextLink Optional If specified, the client will be redirected
|
||||||
|
* to this link after validation.
|
||||||
|
* @param {module:client.callback} callback Optional.
|
||||||
|
* @param {string} identityAccessToken The `access_token` field of the Identity
|
||||||
|
* Server `/account/register` response (see {@link registerWithIdentityServer}).
|
||||||
|
*
|
||||||
|
* @return {module:client.Promise} Resolves: TODO
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
* @throws Error if no identity server is set
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.requestMsisdnToken = async function(
|
||||||
|
phoneCountry,
|
||||||
|
phoneNumber,
|
||||||
|
clientSecret,
|
||||||
|
sendAttempt,
|
||||||
|
nextLink,
|
||||||
|
callback,
|
||||||
|
identityAccessToken,
|
||||||
|
) {
|
||||||
|
const params = {
|
||||||
|
client_secret: clientSecret,
|
||||||
|
country: phoneCountry,
|
||||||
|
phone_number: phoneNumber,
|
||||||
|
send_attempt: sendAttempt,
|
||||||
|
next_link: nextLink,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this._http.idServerRequest(
|
||||||
|
undefined, "POST", "/validate/msisdn/requestToken",
|
||||||
|
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
|
);
|
||||||
|
// TODO: Fold callback into above call once v1 path below is removed
|
||||||
|
if (callback) callback(null, response);
|
||||||
|
return response;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.cors === "rejected" || err.httpStatus === 404) {
|
||||||
|
// Fall back to deprecated v1 API for now
|
||||||
|
// TODO: Remove this path once v2 is only supported version
|
||||||
|
// See https://github.com/vector-im/riot-web/issues/10443
|
||||||
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
|
return await this._http.idServerRequest(
|
||||||
|
callback, "POST", "/validate/msisdn/requestToken",
|
||||||
|
params, httpApi.PREFIX_IDENTITY_V1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (callback) callback(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits a MSISDN token to the identity server
|
||||||
*
|
*
|
||||||
* This is used when submitting the code sent by SMS to a phone number.
|
* This is used when submitting the code sent by SMS to a phone number.
|
||||||
* The ID server has an equivalent API for email but the js-sdk does
|
* The ID server has an equivalent API for email but the js-sdk does
|
||||||
@@ -1740,46 +1987,323 @@ MatrixBaseApis.prototype.requestEmailToken = function(email, clientSecret,
|
|||||||
* @param {string} sid The sid given in the response to requestToken
|
* @param {string} sid The sid given in the response to requestToken
|
||||||
* @param {string} clientSecret A secret binary string generated by the client.
|
* @param {string} clientSecret A secret binary string generated by the client.
|
||||||
* This must be the same value submitted in the requestToken call.
|
* This must be the same value submitted in the requestToken call.
|
||||||
* @param {string} token The token, as enetered by the user.
|
* @param {string} msisdnToken The MSISDN token, as enetered by the user.
|
||||||
|
* @param {string} identityAccessToken The `access_token` field of the Identity
|
||||||
|
* Server `/account/register` response (see {@link registerWithIdentityServer}).
|
||||||
*
|
*
|
||||||
* @return {module:client.Promise} Resolves: Object, currently with no parameters.
|
* @return {module:client.Promise} Resolves: Object, currently with no parameters.
|
||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
* @throws Error if No ID server is set
|
* @throws Error if No ID server is set
|
||||||
*/
|
*/
|
||||||
MatrixBaseApis.prototype.submitMsisdnToken = function(sid, clientSecret, token) {
|
MatrixBaseApis.prototype.submitMsisdnToken = async function(
|
||||||
|
sid,
|
||||||
|
clientSecret,
|
||||||
|
msisdnToken,
|
||||||
|
identityAccessToken,
|
||||||
|
) {
|
||||||
const params = {
|
const params = {
|
||||||
sid: sid,
|
sid: sid,
|
||||||
client_secret: clientSecret,
|
client_secret: clientSecret,
|
||||||
token: token,
|
token: msisdnToken,
|
||||||
};
|
};
|
||||||
return this._http.idServerRequest(
|
|
||||||
|
try {
|
||||||
|
return await this._http.idServerRequest(
|
||||||
|
undefined, "POST", "/validate/msisdn/submitToken",
|
||||||
|
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.cors === "rejected" || err.httpStatus === 404) {
|
||||||
|
// Fall back to deprecated v1 API for now
|
||||||
|
// TODO: Remove this path once v2 is only supported version
|
||||||
|
// See https://github.com/vector-im/riot-web/issues/10443
|
||||||
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
|
return await this._http.idServerRequest(
|
||||||
undefined, "POST", "/validate/msisdn/submitToken",
|
undefined, "POST", "/validate/msisdn/submitToken",
|
||||||
params, httpApi.PREFIX_IDENTITY_V1,
|
params, httpApi.PREFIX_IDENTITY_V1,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits a MSISDN token to an arbitrary URL.
|
||||||
|
*
|
||||||
|
* This is used when submitting the code sent by SMS to a phone number in the
|
||||||
|
* newer 3PID flow where the homeserver validates 3PID ownership (as part of
|
||||||
|
* `requestAdd3pidMsisdnToken`). The homeserver response may include a
|
||||||
|
* `submit_url` to specify where the token should be sent, and this helper can
|
||||||
|
* be used to pass the token to this URL.
|
||||||
|
*
|
||||||
|
* @param {string} url The URL to submit the token to
|
||||||
|
* @param {string} sid The sid given in the response to requestToken
|
||||||
|
* @param {string} clientSecret A secret binary string generated by the client.
|
||||||
|
* This must be the same value submitted in the requestToken call.
|
||||||
|
* @param {string} msisdnToken The MSISDN token, as enetered by the user.
|
||||||
|
*
|
||||||
|
* @return {module:client.Promise} Resolves: Object, currently with no parameters.
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.submitMsisdnTokenOtherUrl = function(
|
||||||
|
url,
|
||||||
|
sid,
|
||||||
|
clientSecret,
|
||||||
|
msisdnToken,
|
||||||
|
) {
|
||||||
|
const params = {
|
||||||
|
sid: sid,
|
||||||
|
client_secret: clientSecret,
|
||||||
|
token: msisdnToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
return this._http.requestOtherUrl(
|
||||||
|
undefined, "POST", url, undefined, params,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the V2 hashing information from the identity server. Primarily useful for
|
||||||
|
* lookups.
|
||||||
|
* @param {string} identityAccessToken The access token for the identity server.
|
||||||
|
* @returns {Promise<object>} The hashing information for the identity server.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.getIdentityHashDetails = function(identityAccessToken) {
|
||||||
|
return this._http.idServerRequest(
|
||||||
|
undefined, "GET", "/hash_details",
|
||||||
|
null, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a hashed lookup of addresses against the identity server. This is
|
||||||
|
* only supported on identity servers which have at least the version 2 API.
|
||||||
|
* @param {Array<Array<string,string>>} addressPairs An array of 2 element arrays.
|
||||||
|
* The first element of each pair is the address, the second is the 3PID medium.
|
||||||
|
* Eg: ["email@example.org", "email"]
|
||||||
|
* @param {string} identityAccessToken The access token for the identity server.
|
||||||
|
* @returns {Promise<Array<{address, mxid}>>} A collection of address mappings to
|
||||||
|
* found MXIDs. Results where no user could be found will not be listed.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.identityHashedLookup = async function(
|
||||||
|
addressPairs, // [["email@example.org", "email"], ["10005550000", "msisdn"]]
|
||||||
|
identityAccessToken,
|
||||||
|
) {
|
||||||
|
const params = {
|
||||||
|
// addresses: ["email@example.org", "10005550000"],
|
||||||
|
// algorithm: "sha256",
|
||||||
|
// pepper: "abc123"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get hash information first before trying to do a lookup
|
||||||
|
const hashes = await this.getIdentityHashDetails(identityAccessToken);
|
||||||
|
if (!hashes || !hashes['lookup_pepper'] || !hashes['algorithms']) {
|
||||||
|
throw new Error("Unsupported identity server: bad response");
|
||||||
|
}
|
||||||
|
|
||||||
|
params['pepper'] = hashes['lookup_pepper'];
|
||||||
|
|
||||||
|
const localMapping = {
|
||||||
|
// hashed identifier => plain text address
|
||||||
|
// For use in this function's return format
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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["algorithm"] = "sha256";
|
||||||
|
} else if (hashes['algorithms'].includes('none')) {
|
||||||
|
params["addresses"] = addressPairs.map(p => {
|
||||||
|
const addr = p[0].toLowerCase(); // lowercase to get consistent hashes
|
||||||
|
const med = p[1].toLowerCase();
|
||||||
|
const unhashed = `${addr} ${med}`;
|
||||||
|
// Map the unhashed values to a known (case-sensitive) address. We use
|
||||||
|
// the case sensitive version because the caller might be expecting that.
|
||||||
|
localMapping[unhashed] = p[0];
|
||||||
|
return unhashed;
|
||||||
|
});
|
||||||
|
params["algorithm"] = "none";
|
||||||
|
} else {
|
||||||
|
throw new Error("Unsupported identity server: unknown hash algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this._http.idServerRequest(
|
||||||
|
undefined, "POST", "/lookup",
|
||||||
|
params, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response || !response['mappings']) return []; // no results
|
||||||
|
|
||||||
|
const foundAddresses = [/* {address: "plain@example.org", mxid} */];
|
||||||
|
for (const hashed of Object.keys(response['mappings'])) {
|
||||||
|
const mxid = response['mappings'][hashed];
|
||||||
|
const plainAddress = localMapping[hashed];
|
||||||
|
if (!plainAddress) {
|
||||||
|
throw new Error("Identity server returned more results than expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
foundAddresses.push({address: plainAddress, mxid});
|
||||||
|
}
|
||||||
|
return foundAddresses;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up the public Matrix ID mapping for a given 3rd party
|
* Looks up the public Matrix ID mapping for a given 3rd party
|
||||||
* identifier from the Identity Server
|
* identifier from the Identity Server
|
||||||
|
*
|
||||||
* @param {string} medium The medium of the threepid, eg. 'email'
|
* @param {string} medium The medium of the threepid, eg. 'email'
|
||||||
* @param {string} address The textual address of the threepid
|
* @param {string} address The textual address of the threepid
|
||||||
* @param {module:client.callback} callback Optional.
|
* @param {module:client.callback} callback Optional.
|
||||||
|
* @param {string} identityAccessToken The `access_token` field of the Identity
|
||||||
|
* Server `/account/register` response (see {@link registerWithIdentityServer}).
|
||||||
|
*
|
||||||
* @return {module:client.Promise} Resolves: A threepid mapping
|
* @return {module:client.Promise} Resolves: A threepid mapping
|
||||||
* object or the empty object if no mapping
|
* object or the empty object if no mapping
|
||||||
* exists
|
* exists
|
||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
*/
|
*/
|
||||||
MatrixBaseApis.prototype.lookupThreePid = function(medium, address, callback) {
|
MatrixBaseApis.prototype.lookupThreePid = async function(
|
||||||
|
medium,
|
||||||
|
address,
|
||||||
|
callback,
|
||||||
|
identityAccessToken,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// Note: we're using the V2 API by calling this function, but our
|
||||||
|
// function contract requires a V1 response. We therefore have to
|
||||||
|
// convert it manually.
|
||||||
|
const response = await this.identityHashedLookup(
|
||||||
|
[[address, medium]], identityAccessToken,
|
||||||
|
);
|
||||||
|
const result = response.find(p => p.address === address);
|
||||||
|
if (!result) {
|
||||||
|
// TODO: Fold callback into above call once v1 path below is removed
|
||||||
|
if (callback) callback(null, {});
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapping = {
|
||||||
|
address,
|
||||||
|
medium,
|
||||||
|
mxid: result.mxid,
|
||||||
|
|
||||||
|
// We can't reasonably fill these parameters:
|
||||||
|
// not_before
|
||||||
|
// not_after
|
||||||
|
// ts
|
||||||
|
// signatures
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Fold callback into above call once v1 path below is removed
|
||||||
|
if (callback) callback(null, mapping);
|
||||||
|
return mapping;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.cors === "rejected" || err.httpStatus === 404) {
|
||||||
|
// Fall back to deprecated v1 API for now
|
||||||
|
// TODO: Remove this path once v2 is only supported version
|
||||||
|
// See https://github.com/vector-im/riot-web/issues/10443
|
||||||
const params = {
|
const params = {
|
||||||
medium: medium,
|
medium: medium,
|
||||||
address: address,
|
address: address,
|
||||||
};
|
};
|
||||||
return this._http.idServerRequest(
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
|
return await this._http.idServerRequest(
|
||||||
callback, "GET", "/lookup",
|
callback, "GET", "/lookup",
|
||||||
params, httpApi.PREFIX_IDENTITY_V1,
|
params, httpApi.PREFIX_IDENTITY_V1,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (callback) callback(err, undefined);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the public Matrix ID mappings for multiple 3PIDs.
|
||||||
|
*
|
||||||
|
* @param {Array.<Array.<string>>} query Array of arrays containing
|
||||||
|
* [medium, address]
|
||||||
|
* @param {string} identityAccessToken The `access_token` field of the Identity
|
||||||
|
* Server `/account/register` response (see {@link registerWithIdentityServer}).
|
||||||
|
*
|
||||||
|
* @return {module:client.Promise} Resolves: Lookup results from IS.
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.bulkLookupThreePids = async function(
|
||||||
|
query,
|
||||||
|
identityAccessToken,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// Note: we're using the V2 API by calling this function, but our
|
||||||
|
// function contract requires a V1 response. We therefore have to
|
||||||
|
// convert it manually.
|
||||||
|
const response = await this.identityHashedLookup(
|
||||||
|
// We have to reverse the query order to get [address, medium] pairs
|
||||||
|
query.map(p => [p[1], p[0]]), identityAccessToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
const v1results = [];
|
||||||
|
for (const mapping of response) {
|
||||||
|
const originalQuery = query.find(p => p[1] === mapping.address);
|
||||||
|
if (!originalQuery) {
|
||||||
|
throw new Error("Identity sever returned unexpected results");
|
||||||
|
}
|
||||||
|
|
||||||
|
v1results.push([
|
||||||
|
originalQuery[0], // medium
|
||||||
|
mapping.address,
|
||||||
|
mapping.mxid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {threepids: v1results};
|
||||||
|
} catch (err) {
|
||||||
|
if (err.cors === "rejected" || err.httpStatus === 404) {
|
||||||
|
// Fall back to deprecated v1 API for now
|
||||||
|
// TODO: Remove this path once v2 is only supported version
|
||||||
|
// See https://github.com/vector-im/riot-web/issues/10443
|
||||||
|
const params = {
|
||||||
|
threepids: query,
|
||||||
|
};
|
||||||
|
logger.warn("IS doesn't support v2, falling back to deprecated v1");
|
||||||
|
return await this._http.idServerRequest(
|
||||||
|
undefined, "POST", "/bulk_lookup", params,
|
||||||
|
httpApi.PREFIX_IDENTITY_V1, identityAccessToken,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get account info from the Identity Server. This is useful as a neutral check
|
||||||
|
* to verify that other APIs are likely to approve access by testing that the
|
||||||
|
* token is valid, terms have been agreed, etc.
|
||||||
|
*
|
||||||
|
* @param {string} identityAccessToken The `access_token` field of the Identity
|
||||||
|
* Server `/account/register` response (see {@link registerWithIdentityServer}).
|
||||||
|
*
|
||||||
|
* @return {module:client.Promise} Resolves: an object with account info.
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.getIdentityAccount = function(
|
||||||
|
identityAccessToken,
|
||||||
|
) {
|
||||||
|
return this._http.idServerRequest(
|
||||||
|
undefined, "GET", "/account",
|
||||||
|
undefined, httpApi.PREFIX_IDENTITY_V2, identityAccessToken,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// Direct-to-device messaging
|
// Direct-to-device messaging
|
||||||
// ==========================
|
// ==========================
|
||||||
@@ -1806,10 +2330,7 @@ MatrixBaseApis.prototype.sendToDevice = function(
|
|||||||
messages: contentMap,
|
messages: contentMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(undefined, "PUT", path, undefined, body);
|
||||||
undefined, "PUT", path, undefined, body,
|
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Third party Lookup API
|
// Third party Lookup API
|
||||||
@@ -1821,9 +2342,8 @@ MatrixBaseApis.prototype.sendToDevice = function(
|
|||||||
* @return {module:client.Promise} Resolves to the result object
|
* @return {module:client.Promise} Resolves to the result object
|
||||||
*/
|
*/
|
||||||
MatrixBaseApis.prototype.getThirdpartyProtocols = function() {
|
MatrixBaseApis.prototype.getThirdpartyProtocols = function() {
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(
|
||||||
undefined, "GET", "/thirdparty/protocols", undefined, undefined,
|
undefined, "GET", "/thirdparty/protocols", undefined, undefined,
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
).then((response) => {
|
).then((response) => {
|
||||||
// sanity check
|
// sanity check
|
||||||
if (!response || typeof(response) !== 'object') {
|
if (!response || typeof(response) !== 'object') {
|
||||||
@@ -1848,10 +2368,7 @@ MatrixBaseApis.prototype.getThirdpartyLocation = function(protocol, params) {
|
|||||||
$protocol: protocol,
|
$protocol: protocol,
|
||||||
});
|
});
|
||||||
|
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(undefined, "GET", path, params, undefined);
|
||||||
undefined, "GET", path, params, undefined,
|
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1867,12 +2384,45 @@ MatrixBaseApis.prototype.getThirdpartyUser = function(protocol, params) {
|
|||||||
$protocol: protocol,
|
$protocol: protocol,
|
||||||
});
|
});
|
||||||
|
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(undefined, "GET", path, params, undefined);
|
||||||
undefined, "GET", path, params, undefined,
|
};
|
||||||
httpApi.PREFIX_UNSTABLE,
|
|
||||||
|
MatrixBaseApis.prototype.getTerms = function(serviceType, baseUrl) {
|
||||||
|
const url = termsUrlForService(serviceType, baseUrl);
|
||||||
|
return this._http.requestOtherUrl(
|
||||||
|
undefined, 'GET', url,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MatrixBaseApis.prototype.agreeToTerms = function(
|
||||||
|
serviceType, baseUrl, accessToken, termsUrls,
|
||||||
|
) {
|
||||||
|
const url = termsUrlForService(serviceType, baseUrl);
|
||||||
|
const headers = {
|
||||||
|
Authorization: "Bearer " + accessToken,
|
||||||
|
};
|
||||||
|
return this._http.requestOtherUrl(
|
||||||
|
undefined, 'POST', url, null, { user_accepts: termsUrls }, { headers },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports an event as inappropriate to the server, which may then notify the appropriate people.
|
||||||
|
* @param {string} roomId The room in which the event being reported is located.
|
||||||
|
* @param {string} eventId The event to report.
|
||||||
|
* @param {number} score The score to rate this content as where -100 is most offensive and 0 is inoffensive.
|
||||||
|
* @param {string} reason The reason the content is being reported. May be blank.
|
||||||
|
* @returns {module:client.Promise} Resolves to an empty object if successful
|
||||||
|
*/
|
||||||
|
MatrixBaseApis.prototype.reportEvent = function(roomId, eventId, score, reason) {
|
||||||
|
const path = utils.encodeUri("/rooms/$roomId/report/$eventId", {
|
||||||
|
$roomId: roomId,
|
||||||
|
$eventId: eventId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._http.authedRequest(undefined, "POST", path, null, {score, reason});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MatrixBaseApis object
|
* MatrixBaseApis object
|
||||||
*/
|
*/
|
||||||
|
|||||||
539
src/client.js
539
src/client.js
@@ -2,6 +2,7 @@
|
|||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2018-2019 New Vector Ltd
|
Copyright 2018-2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -45,7 +46,7 @@ const olmlib = require("./crypto/olmlib");
|
|||||||
|
|
||||||
import ReEmitter from './ReEmitter';
|
import ReEmitter from './ReEmitter';
|
||||||
import RoomList from './crypto/RoomList';
|
import RoomList from './crypto/RoomList';
|
||||||
import logger from '../src/logger';
|
import logger from './logger';
|
||||||
|
|
||||||
import Crypto from './crypto';
|
import Crypto from './crypto';
|
||||||
import { isCryptoAvailable } from './crypto';
|
import { isCryptoAvailable } from './crypto';
|
||||||
@@ -107,6 +108,15 @@ function keyFromRecoverySession(session, decryptionKey) {
|
|||||||
*
|
*
|
||||||
* @param {string} opts.userId The user ID for this user.
|
* @param {string} opts.userId The user ID for this user.
|
||||||
*
|
*
|
||||||
|
* @param {IdentityServerProvider} [opts.identityServer]
|
||||||
|
* Optional. A provider object with one function `getAccessToken`, which is a
|
||||||
|
* callback that returns a Promise<String> of an identity access token to supply
|
||||||
|
* with identity requests. If the object is unset, no access token will be
|
||||||
|
* supplied.
|
||||||
|
* See also https://github.com/vector-im/riot-web/issues/10615 which seeks to
|
||||||
|
* replace the previous approach of manual access tokens params with this
|
||||||
|
* callback throughout the SDK.
|
||||||
|
*
|
||||||
* @param {Object=} opts.store
|
* @param {Object=} opts.store
|
||||||
* The data store used for sync data from the homeserver. If not specified,
|
* The data store used for sync data from the homeserver. If not specified,
|
||||||
* this client will not store any HTTP responses. The `createClient` helper
|
* this client will not store any HTTP responses. The `createClient` helper
|
||||||
@@ -158,17 +168,17 @@ function keyFromRecoverySession(session, decryptionKey) {
|
|||||||
* that the application can handle. Each element should be an item from {@link
|
* that the application can handle. Each element should be an item from {@link
|
||||||
* module:crypto~verificationMethods verificationMethods}, or a class that
|
* module:crypto~verificationMethods verificationMethods}, or a class that
|
||||||
* implements the {$link module:crypto/verification/Base verifier interface}.
|
* implements the {$link module:crypto/verification/Base verifier interface}.
|
||||||
|
*
|
||||||
|
* @param {boolean} [opts.forceTURN]
|
||||||
|
* Optional. Whether relaying calls through a TURN server should be forced.
|
||||||
|
*
|
||||||
|
* @param {boolean} [opts.fallbackICEServerAllowed]
|
||||||
|
* Optional. Whether to allow a fallback ICE server should be used for negotiating a
|
||||||
|
* WebRTC connection if the homeserver doesn't provide any servers. Defaults to false.
|
||||||
*/
|
*/
|
||||||
function MatrixClient(opts) {
|
function MatrixClient(opts) {
|
||||||
// Allow trailing slash in HS url
|
opts.baseUrl = utils.ensureNoTrailingSlash(opts.baseUrl);
|
||||||
if (opts.baseUrl && opts.baseUrl.endsWith("/")) {
|
opts.idBaseUrl = utils.ensureNoTrailingSlash(opts.idBaseUrl);
|
||||||
opts.baseUrl = opts.baseUrl.substr(0, opts.baseUrl.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow trailing slash in IS url
|
|
||||||
if (opts.idBaseUrl && opts.idBaseUrl.endsWith("/")) {
|
|
||||||
opts.idBaseUrl = opts.idBaseUrl.substr(0, opts.idBaseUrl.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
MatrixBaseApis.call(this, opts);
|
MatrixBaseApis.call(this, opts);
|
||||||
|
|
||||||
@@ -227,6 +237,7 @@ function MatrixClient(opts) {
|
|||||||
this._verificationMethods = opts.verificationMethods;
|
this._verificationMethods = opts.verificationMethods;
|
||||||
|
|
||||||
this._forceTURN = opts.forceTURN || false;
|
this._forceTURN = opts.forceTURN || false;
|
||||||
|
this._fallbackICEServerAllowed = opts.fallbackICEServerAllowed || false;
|
||||||
|
|
||||||
// List of which rooms have encryption enabled: separate from crypto because
|
// List of which rooms have encryption enabled: separate from crypto because
|
||||||
// we still want to know which rooms are encrypted even if crypto is disabled:
|
// we still want to know which rooms are encrypted even if crypto is disabled:
|
||||||
@@ -236,7 +247,9 @@ function MatrixClient(opts) {
|
|||||||
// The pushprocessor caches useful things, so keep one and re-use it
|
// The pushprocessor caches useful things, so keep one and re-use it
|
||||||
this._pushProcessor = new PushProcessor(this);
|
this._pushProcessor = new PushProcessor(this);
|
||||||
|
|
||||||
this._serverSupportsLazyLoading = null;
|
// Cache of the server's /versions response
|
||||||
|
// TODO: This should expire: https://github.com/matrix-org/matrix-js-sdk/issues/1020
|
||||||
|
this._serverVersionsCache = null;
|
||||||
|
|
||||||
this._cachedCapabilities = null; // { capabilities: {}, lastUpdated: timestamp }
|
this._cachedCapabilities = null; // { capabilities: {}, lastUpdated: timestamp }
|
||||||
|
|
||||||
@@ -278,6 +291,48 @@ function MatrixClient(opts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Like above, we have to listen for read receipts from ourselves in order to
|
||||||
|
// correctly handle notification counts on encrypted rooms.
|
||||||
|
// This fixes https://github.com/vector-im/riot-web/issues/9421
|
||||||
|
this.on("Room.receipt", (event, room) => {
|
||||||
|
if (room && this.isRoomEncrypted(room.roomId)) {
|
||||||
|
// Figure out if we've read something or if it's just informational
|
||||||
|
const content = event.getContent();
|
||||||
|
const isSelf = Object.keys(content).filter(eid => {
|
||||||
|
return Object.keys(content[eid]['m.read']).includes(this.getUserId());
|
||||||
|
}).length > 0;
|
||||||
|
|
||||||
|
if (!isSelf) return;
|
||||||
|
|
||||||
|
// Work backwards to determine how many events are unread. We also set
|
||||||
|
// a limit for how back we'll look to avoid spinning CPU for too long.
|
||||||
|
// If we hit the limit, we assume the count is unchanged.
|
||||||
|
const maxHistory = 20;
|
||||||
|
const events = room.getLiveTimeline().getEvents();
|
||||||
|
|
||||||
|
let highlightCount = 0;
|
||||||
|
|
||||||
|
for (let i = events.length - 1; i >= 0; i--) {
|
||||||
|
if (i === events.length - maxHistory) return; // limit reached
|
||||||
|
|
||||||
|
const event = events[i];
|
||||||
|
|
||||||
|
if (room.hasUserReadEvent(this.getUserId(), event.getId())) {
|
||||||
|
// If the user has read the event, then the counting is done.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightCount += this.getPushActionsForEvent(
|
||||||
|
event,
|
||||||
|
).tweaks.highlight ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: we don't need to handle 'total' notifications because the counts
|
||||||
|
// will come from the server.
|
||||||
|
room.setUnreadNotificationCount("highlight", highlightCount);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
utils.inherits(MatrixClient, EventEmitter);
|
utils.inherits(MatrixClient, EventEmitter);
|
||||||
utils.extend(MatrixClient.prototype, MatrixBaseApis.prototype);
|
utils.extend(MatrixClient.prototype, MatrixBaseApis.prototype);
|
||||||
@@ -734,6 +789,40 @@ async function _setDeviceVerification(
|
|||||||
client.emit("deviceVerificationChanged", userId, deviceId, dev);
|
client.emit("deviceVerificationChanged", userId, deviceId, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a key verification from another user, using a DM.
|
||||||
|
*
|
||||||
|
* @param {string} userId the user to request verification with
|
||||||
|
* @param {string} roomId the room to use for verification
|
||||||
|
* @param {Array} methods array of verification methods to use. Defaults to
|
||||||
|
* all known methods
|
||||||
|
*
|
||||||
|
* @returns {Promise<module:crypto/verification/Base>} resolves to a verifier
|
||||||
|
* when the request is accepted by the other user
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.requestVerificationDM = function(userId, roomId, methods) {
|
||||||
|
if (this._crypto === null) {
|
||||||
|
throw new Error("End-to-end encryption disabled");
|
||||||
|
}
|
||||||
|
return this._crypto.requestVerificationDM(userId, roomId, methods);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a key verification request from a DM.
|
||||||
|
*
|
||||||
|
* @param {module:models/event~MatrixEvent} event the verification request
|
||||||
|
* that is accepted
|
||||||
|
* @param {string} method the verification mmethod to use
|
||||||
|
*
|
||||||
|
* @returns {module:crypto/verification/Base} a verifier
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.acceptVerificationDM = function(event, method) {
|
||||||
|
if (this._crypto === null) {
|
||||||
|
throw new Error("End-to-end encryption disabled");
|
||||||
|
}
|
||||||
|
return this._crypto.acceptVerificationDM(event, method);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request a key verification from another user.
|
* Request a key verification from another user.
|
||||||
*
|
*
|
||||||
@@ -1107,7 +1196,8 @@ MatrixClient.prototype.checkKeyBackup = function() {
|
|||||||
*/
|
*/
|
||||||
MatrixClient.prototype.getKeyBackupVersion = function() {
|
MatrixClient.prototype.getKeyBackupVersion = function() {
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "GET", "/room_keys/version",
|
undefined, "GET", "/room_keys/version", undefined, undefined,
|
||||||
|
{prefix: httpApi.PREFIX_UNSTABLE},
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
if (res.algorithm !== olmlib.MEGOLM_BACKUP_ALGORITHM) {
|
if (res.algorithm !== olmlib.MEGOLM_BACKUP_ALGORITHM) {
|
||||||
const err = "Unknown backup algorithm: " + res.algorithm;
|
const err = "Unknown backup algorithm: " + res.algorithm;
|
||||||
@@ -1262,6 +1352,7 @@ MatrixClient.prototype.createKeyBackupVersion = async function(info) {
|
|||||||
|
|
||||||
const res = await this._http.authedRequest(
|
const res = await this._http.authedRequest(
|
||||||
undefined, "POST", "/room_keys/version", undefined, data,
|
undefined, "POST", "/room_keys/version", undefined, data,
|
||||||
|
{prefix: httpApi.PREFIX_UNSTABLE},
|
||||||
);
|
);
|
||||||
this.enableKeyBackup({
|
this.enableKeyBackup({
|
||||||
algorithm: info.algorithm,
|
algorithm: info.algorithm,
|
||||||
@@ -1289,6 +1380,7 @@ MatrixClient.prototype.deleteKeyBackupVersion = function(version) {
|
|||||||
|
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "DELETE", path, undefined, undefined,
|
undefined, "DELETE", path, undefined, undefined,
|
||||||
|
{prefix: httpApi.PREFIX_UNSTABLE},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1330,6 +1422,7 @@ MatrixClient.prototype.sendKeyBackup = function(roomId, sessionId, version, data
|
|||||||
const path = this._makeKeyBackupPath(roomId, sessionId, version);
|
const path = this._makeKeyBackupPath(roomId, sessionId, version);
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "PUT", path.path, path.queryData, data,
|
undefined, "PUT", path.path, path.queryData, data,
|
||||||
|
{prefix: httpApi.PREFIX_UNSTABLE},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1345,6 +1438,19 @@ MatrixClient.prototype.scheduleAllGroupSessionsForBackup = async function() {
|
|||||||
await this._crypto.scheduleAllGroupSessionsForBackup();
|
await this._crypto.scheduleAllGroupSessionsForBackup();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks all group sessions as needing to be backed up without scheduling
|
||||||
|
* them to upload in the background.
|
||||||
|
* @returns {Promise<int>} Resolves to the number of sessions requiring a backup.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.flagAllGroupSessionsForBackup = function() {
|
||||||
|
if (this._crypto === null) {
|
||||||
|
throw new Error("End-to-end encryption disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._crypto.flagAllGroupSessionsForBackup();
|
||||||
|
};
|
||||||
|
|
||||||
MatrixClient.prototype.isValidRecoveryKey = function(recoveryKey) {
|
MatrixClient.prototype.isValidRecoveryKey = function(recoveryKey) {
|
||||||
try {
|
try {
|
||||||
decodeRecoveryKey(recoveryKey);
|
decodeRecoveryKey(recoveryKey);
|
||||||
@@ -1404,7 +1510,8 @@ MatrixClient.prototype._restoreKeyBackup = function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "GET", path.path, path.queryData,
|
undefined, "GET", path.path, path.queryData, undefined,
|
||||||
|
{prefix: httpApi.PREFIX_UNSTABLE},
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
if (res.rooms) {
|
if (res.rooms) {
|
||||||
for (const [roomId, roomData] of Object.entries(res.rooms)) {
|
for (const [roomId, roomData] of Object.entries(res.rooms)) {
|
||||||
@@ -1453,7 +1560,8 @@ MatrixClient.prototype.deleteKeysFromBackup = function(roomId, sessionId, versio
|
|||||||
|
|
||||||
const path = this._makeKeyBackupPath(roomId, sessionId, version);
|
const path = this._makeKeyBackupPath(roomId, sessionId, version);
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "DELETE", path.path, path.queryData,
|
undefined, "DELETE", path.path, path.queryData, undefined,
|
||||||
|
{prefix: httpApi.PREFIX_UNSTABLE},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1487,8 +1595,10 @@ MatrixClient.prototype.getGroups = function() {
|
|||||||
* @return {module:client.Promise} Resolves with an object containing the config.
|
* @return {module:client.Promise} Resolves with an object containing the config.
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.getMediaConfig = function(callback) {
|
MatrixClient.prototype.getMediaConfig = function(callback) {
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(
|
||||||
callback, "GET", "/config", undefined, undefined, httpApi.PREFIX_MEDIA_R0,
|
callback, "GET", "/config", undefined, undefined, {
|
||||||
|
prefix: httpApi.PREFIX_MEDIA_R0,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2043,6 +2153,20 @@ function _encryptEventIfNeeded(client, event, room) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.getType() === "m.reaction") {
|
||||||
|
// For reactions, there is a very little gained by encrypting the entire
|
||||||
|
// event, as relation data is already kept in the clear. Event
|
||||||
|
// encryption for a reaction effectively only obscures the event type,
|
||||||
|
// but the purpose is still obvious from the relation data, so nothing
|
||||||
|
// is really gained. It also causes quite a few problems, such as:
|
||||||
|
// * triggers notifications via default push rules
|
||||||
|
// * prevents server-side bundling for reactions
|
||||||
|
// The reaction key / content / emoji value does warrant encrypting, but
|
||||||
|
// this will be handled separately by encrypting just this value.
|
||||||
|
// See https://github.com/matrix-org/matrix-doc/pull/1849#pullrequestreview-248763642
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!client._crypto) {
|
if (!client._crypto) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"This room is configured to use encryption, but your client does " +
|
"This room is configured to use encryption, but your client does " +
|
||||||
@@ -2052,6 +2176,21 @@ function _encryptEventIfNeeded(client, event, room) {
|
|||||||
|
|
||||||
return client._crypto.encryptEvent(event, room);
|
return client._crypto.encryptEvent(event, room);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns the eventType that should be used taking encryption into account
|
||||||
|
* for a given eventType.
|
||||||
|
* @param {MatrixClient} client the client
|
||||||
|
* @param {string} roomId the room for the events `eventType` relates to
|
||||||
|
* @param {string} eventType the event type
|
||||||
|
* @return {string} the event type taking encryption into account
|
||||||
|
*/
|
||||||
|
function _getEncryptedIfNeededEventType(client, roomId, eventType) {
|
||||||
|
if (eventType === "m.reaction") {
|
||||||
|
return eventType;
|
||||||
|
}
|
||||||
|
const isEncrypted = client.isRoomEncrypted(roomId);
|
||||||
|
return isEncrypted ? "m.room.encrypted" : eventType;
|
||||||
|
}
|
||||||
|
|
||||||
function _updatePendingEventStatus(room, event, newStatus) {
|
function _updatePendingEventStatus(room, event, newStatus) {
|
||||||
if (room) {
|
if (room) {
|
||||||
@@ -2267,11 +2406,17 @@ MatrixClient.prototype.sendHtmlEmote = function(roomId, body, htmlBody, callback
|
|||||||
* Send a receipt.
|
* Send a receipt.
|
||||||
* @param {Event} event The event being acknowledged
|
* @param {Event} event The event being acknowledged
|
||||||
* @param {string} receiptType The kind of receipt e.g. "m.read"
|
* @param {string} receiptType The kind of receipt e.g. "m.read"
|
||||||
|
* @param {object} opts Additional content to send alongside the receipt.
|
||||||
* @param {module:client.callback} callback Optional.
|
* @param {module:client.callback} callback Optional.
|
||||||
* @return {module:client.Promise} Resolves: TODO
|
* @return {module:client.Promise} Resolves: TODO
|
||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.sendReceipt = function(event, receiptType, callback) {
|
MatrixClient.prototype.sendReceipt = function(event, receiptType, opts, callback) {
|
||||||
|
if (typeof(opts) === 'function') {
|
||||||
|
callback = opts;
|
||||||
|
opts = {};
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isGuest()) {
|
if (this.isGuest()) {
|
||||||
return Promise.resolve({}); // guests cannot send receipts so don't bother.
|
return Promise.resolve({}); // guests cannot send receipts so don't bother.
|
||||||
}
|
}
|
||||||
@@ -2282,7 +2427,7 @@ MatrixClient.prototype.sendReceipt = function(event, receiptType, callback) {
|
|||||||
$eventId: event.getId(),
|
$eventId: event.getId(),
|
||||||
});
|
});
|
||||||
const promise = this._http.authedRequest(
|
const promise = this._http.authedRequest(
|
||||||
callback, "POST", path, undefined, {},
|
callback, "POST", path, undefined, opts || {},
|
||||||
);
|
);
|
||||||
|
|
||||||
const room = this.getRoom(event.getRoomId());
|
const room = this.getRoom(event.getRoomId());
|
||||||
@@ -2295,12 +2440,32 @@ MatrixClient.prototype.sendReceipt = function(event, receiptType, callback) {
|
|||||||
/**
|
/**
|
||||||
* Send a read receipt.
|
* Send a read receipt.
|
||||||
* @param {Event} event The event that has been read.
|
* @param {Event} event The event that has been read.
|
||||||
|
* @param {object} opts The options for the read receipt.
|
||||||
|
* @param {boolean} opts.hidden True to prevent the receipt from being sent to
|
||||||
|
* other users and homeservers. Default false (send to everyone). <b>This
|
||||||
|
* property is unstable and may change in the future.</b>
|
||||||
* @param {module:client.callback} callback Optional.
|
* @param {module:client.callback} callback Optional.
|
||||||
* @return {module:client.Promise} Resolves: TODO
|
* @return {module:client.Promise} Resolves: TODO
|
||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.sendReadReceipt = function(event, callback) {
|
MatrixClient.prototype.sendReadReceipt = async function(event, opts, callback) {
|
||||||
return this.sendReceipt(event, "m.read", callback);
|
if (typeof(opts) === 'function') {
|
||||||
|
callback = opts;
|
||||||
|
opts = {};
|
||||||
|
}
|
||||||
|
if (!opts) opts = {};
|
||||||
|
|
||||||
|
const eventId = event.getId();
|
||||||
|
const room = this.getRoom(event.getRoomId());
|
||||||
|
if (room && room.hasPendingEvent(eventId)) {
|
||||||
|
throw new Error(`Cannot set read receipt to a pending event (${eventId})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const addlContent = {
|
||||||
|
"m.hidden": Boolean(opts.hidden),
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.sendReceipt(event, "m.read", addlContent, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2309,26 +2474,36 @@ MatrixClient.prototype.sendReadReceipt = function(event, callback) {
|
|||||||
* and displayed as a horizontal line in the timeline that is visually distinct to the
|
* and displayed as a horizontal line in the timeline that is visually distinct to the
|
||||||
* position of the user's own read receipt.
|
* position of the user's own read receipt.
|
||||||
* @param {string} roomId ID of the room that has been read
|
* @param {string} roomId ID of the room that has been read
|
||||||
* @param {string} eventId ID of the event that has been read
|
* @param {string} rmEventId ID of the event that has been read
|
||||||
* @param {string} rrEvent the event tracked by the read receipt. This is here for
|
* @param {string} rrEvent the event tracked by the read receipt. This is here for
|
||||||
* convenience because the RR and the RM are commonly updated at the same time as each
|
* convenience because the RR and the RM are commonly updated at the same time as each
|
||||||
* other. The local echo of this receipt will be done if set. Optional.
|
* other. The local echo of this receipt will be done if set. Optional.
|
||||||
|
* @param {object} opts Options for the read markers
|
||||||
|
* @param {object} opts.hidden True to hide the receipt from other users and homeservers.
|
||||||
|
* <b>This property is unstable and may change in the future.</b>
|
||||||
* @return {module:client.Promise} Resolves: the empty object, {}.
|
* @return {module:client.Promise} Resolves: the empty object, {}.
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.setRoomReadMarkers = function(roomId, eventId, rrEvent) {
|
MatrixClient.prototype.setRoomReadMarkers = async function(
|
||||||
const rmEventId = eventId;
|
roomId, rmEventId, rrEvent, opts,
|
||||||
let rrEventId;
|
) {
|
||||||
|
const room = this.getRoom(roomId);
|
||||||
|
if (room && room.hasPendingEvent(rmEventId)) {
|
||||||
|
throw new Error(`Cannot set read marker to a pending event (${rmEventId})`);
|
||||||
|
}
|
||||||
|
|
||||||
// Add the optional RR update, do local echo like `sendReceipt`
|
// Add the optional RR update, do local echo like `sendReceipt`
|
||||||
|
let rrEventId;
|
||||||
if (rrEvent) {
|
if (rrEvent) {
|
||||||
rrEventId = rrEvent.getId();
|
rrEventId = rrEvent.getId();
|
||||||
const room = this.getRoom(roomId);
|
if (room && room.hasPendingEvent(rrEventId)) {
|
||||||
|
throw new Error(`Cannot set read receipt to a pending event (${rrEventId})`);
|
||||||
|
}
|
||||||
if (room) {
|
if (room) {
|
||||||
room._addLocalEchoReceipt(this.credentials.userId, rrEvent, "m.read");
|
room._addLocalEchoReceipt(this.credentials.userId, rrEvent, "m.read");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.setRoomReadMarkersHttpRequest(roomId, rmEventId, rrEventId);
|
return this.setRoomReadMarkersHttpRequest(roomId, rmEventId, rrEventId, opts);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2354,11 +2529,13 @@ MatrixClient.prototype.getUrlPreview = function(url, ts, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
return this._http.authedRequestWithPrefix(
|
return this._http.authedRequest(
|
||||||
callback, "GET", "/preview_url", {
|
callback, "GET", "/preview_url", {
|
||||||
url: url,
|
url: url,
|
||||||
ts: ts,
|
ts: ts,
|
||||||
}, undefined, httpApi.PREFIX_MEDIA_R0,
|
}, undefined, {
|
||||||
|
prefix: httpApi.PREFIX_MEDIA_R0,
|
||||||
|
},
|
||||||
).then(function(response) {
|
).then(function(response) {
|
||||||
// TODO: expire cache occasionally
|
// TODO: expire cache occasionally
|
||||||
self.urlPreviewCache[key] = response;
|
self.urlPreviewCache[key] = response;
|
||||||
@@ -2448,6 +2625,7 @@ MatrixClient.prototype.getRoomUpgradeHistory = function(roomId, verifyLinks=fals
|
|||||||
while (tombstoneEvent) {
|
while (tombstoneEvent) {
|
||||||
const refRoom = this.getRoom(tombstoneEvent.getContent()['replacement_room']);
|
const refRoom = this.getRoom(tombstoneEvent.getContent()['replacement_room']);
|
||||||
if (!refRoom) break; // end of the chain
|
if (!refRoom) break; // end of the chain
|
||||||
|
if (refRoom.roomId === currentRoom.roomId) break; // Tombstone is referencing it's own room
|
||||||
|
|
||||||
if (verifyLinks) {
|
if (verifyLinks) {
|
||||||
createEvent = refRoom.currentState.getStateEvents("m.room.create", "");
|
createEvent = refRoom.currentState.getStateEvents("m.room.create", "");
|
||||||
@@ -2459,6 +2637,12 @@ MatrixClient.prototype.getRoomUpgradeHistory = function(roomId, verifyLinks=fals
|
|||||||
|
|
||||||
// Push to the end because we're looking forwards
|
// Push to the end because we're looking forwards
|
||||||
upgradeHistory.push(refRoom);
|
upgradeHistory.push(refRoom);
|
||||||
|
const roomIds = new Set(upgradeHistory.map((ref) => ref.roomId));
|
||||||
|
if (roomIds.size < upgradeHistory.length) {
|
||||||
|
// The last room added to the list introduced a previous roomId
|
||||||
|
// To avoid recursion, return the last rooms - 1
|
||||||
|
return upgradeHistory.slice(0, upgradeHistory.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Set the current room to the reference room so we know where we're at
|
// Set the current room to the reference room so we know where we're at
|
||||||
currentRoom = refRoom;
|
currentRoom = refRoom;
|
||||||
@@ -2503,7 +2687,12 @@ MatrixClient.prototype.inviteByEmail = function(roomId, email, callback) {
|
|||||||
* @return {module:client.Promise} Resolves: TODO
|
* @return {module:client.Promise} Resolves: TODO
|
||||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.inviteByThreePid = function(roomId, medium, address, callback) {
|
MatrixClient.prototype.inviteByThreePid = async function(
|
||||||
|
roomId,
|
||||||
|
medium,
|
||||||
|
address,
|
||||||
|
callback,
|
||||||
|
) {
|
||||||
const path = utils.encodeUri(
|
const path = utils.encodeUri(
|
||||||
"/rooms/$roomId/invite",
|
"/rooms/$roomId/invite",
|
||||||
{ $roomId: roomId },
|
{ $roomId: roomId },
|
||||||
@@ -2516,12 +2705,24 @@ MatrixClient.prototype.inviteByThreePid = function(roomId, medium, address, call
|
|||||||
errcode: "ORG.MATRIX.JSSDK_MISSING_PARAM",
|
errcode: "ORG.MATRIX.JSSDK_MISSING_PARAM",
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
const params = {
|
||||||
return this._http.authedRequest(callback, "POST", path, undefined, {
|
|
||||||
id_server: identityServerUrl,
|
id_server: identityServerUrl,
|
||||||
medium: medium,
|
medium: medium,
|
||||||
address: address,
|
address: address,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.identityServer &&
|
||||||
|
this.identityServer.getAccessToken &&
|
||||||
|
await this.doesServerAcceptIdentityAccessToken()
|
||||||
|
) {
|
||||||
|
const identityAccessToken = await this.identityServer.getAccessToken();
|
||||||
|
if (identityAccessToken) {
|
||||||
|
params.id_access_token = identityAccessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._http.authedRequest(callback, "POST", path, undefined, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3150,9 +3351,8 @@ MatrixClient.prototype.paginateEventTimeline = function(eventTimeline, opts) {
|
|||||||
params.from = token;
|
params.from = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
promise =
|
promise = this._http.authedRequest(
|
||||||
this._http.authedRequestWithPrefix(undefined, "GET", path, params,
|
undefined, "GET", path, params, undefined,
|
||||||
undefined, httpApi.PREFIX_UNSTABLE,
|
|
||||||
).then(function(res) {
|
).then(function(res) {
|
||||||
const token = res.next_token;
|
const token = res.next_token;
|
||||||
const matrixEvents = [];
|
const matrixEvents = [];
|
||||||
@@ -3311,14 +3511,9 @@ MatrixClient.prototype.setGuestAccess = function(roomId, opts) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests an email verification token for the purposes of registration.
|
* Requests an email verification token for the purposes of registration.
|
||||||
* This API proxies the Identity Server /validate/email/requestToken API,
|
* This API requests a token from the homeserver.
|
||||||
* adding registration-specific behaviour. Specifically, if an account with
|
* The doesServerRequireIdServerParam() method can be used to determine if
|
||||||
* the given email address already exists, it will either send an email
|
* the server requires the id_server parameter to be provided.
|
||||||
* to the address informing them of this or return M_THREEPID_IN_USE
|
|
||||||
* (which one is up to the Home Server).
|
|
||||||
*
|
|
||||||
* requestEmailToken calls the equivalent API directly on the ID server,
|
|
||||||
* therefore bypassing the registration-specific logic.
|
|
||||||
*
|
*
|
||||||
* Parameters and return value are as for requestEmailToken
|
* Parameters and return value are as for requestEmailToken
|
||||||
|
|
||||||
@@ -3343,8 +3538,9 @@ MatrixClient.prototype.requestRegisterEmailToken = function(email, clientSecret,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests a text message verification token for the purposes of registration.
|
* Requests a text message verification token for the purposes of registration.
|
||||||
* This API proxies the Identity Server /validate/msisdn/requestToken API,
|
* This API requests a token from the homeserver.
|
||||||
* adding registration-specific behaviour, as with requestRegisterEmailToken.
|
* The doesServerRequireIdServerParam() method can be used to determine if
|
||||||
|
* the server requires the id_server parameter to be provided.
|
||||||
*
|
*
|
||||||
* @param {string} phoneCountry The ISO 3166-1 alpha-2 code for the country in which
|
* @param {string} phoneCountry The ISO 3166-1 alpha-2 code for the country in which
|
||||||
* phoneNumber should be parsed relative to.
|
* phoneNumber should be parsed relative to.
|
||||||
@@ -3371,15 +3567,13 @@ MatrixClient.prototype.requestRegisterMsisdnToken = function(phoneCountry, phone
|
|||||||
/**
|
/**
|
||||||
* Requests an email verification token for the purposes of adding a
|
* Requests an email verification token for the purposes of adding a
|
||||||
* third party identifier to an account.
|
* third party identifier to an account.
|
||||||
* This API proxies the Identity Server /validate/email/requestToken API,
|
* This API requests a token from the homeserver.
|
||||||
* adding specific behaviour for the addition of email addresses to an
|
* The doesServerRequireIdServerParam() method can be used to determine if
|
||||||
* account. Specifically, if an account with
|
* the server requires the id_server parameter to be provided.
|
||||||
* the given email address already exists, it will either send an email
|
* If an account with the given email address already exists and is
|
||||||
* to the address informing them of this or return M_THREEPID_IN_USE
|
* associated with an account other than the one the user is authed as,
|
||||||
* (which one is up to the Home Server).
|
* it will either send an email to the address informing them of this
|
||||||
*
|
* or return M_THREEPID_IN_USE (which one is up to the Home Server).
|
||||||
* requestEmailToken calls the equivalent API directly on the ID server,
|
|
||||||
* therefore bypassing the email addition specific logic.
|
|
||||||
*
|
*
|
||||||
* @param {string} email As requestEmailToken
|
* @param {string} email As requestEmailToken
|
||||||
* @param {string} clientSecret As requestEmailToken
|
* @param {string} clientSecret As requestEmailToken
|
||||||
@@ -3495,15 +3689,30 @@ MatrixClient.prototype.requestPasswordMsisdnToken = function(phoneCountry, phone
|
|||||||
* @param {object} params Parameters for the POST request
|
* @param {object} params Parameters for the POST request
|
||||||
* @return {module:client.Promise} Resolves: As requestEmailToken
|
* @return {module:client.Promise} Resolves: As requestEmailToken
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype._requestTokenFromEndpoint = function(endpoint, params) {
|
MatrixClient.prototype._requestTokenFromEndpoint = async function(endpoint, params) {
|
||||||
const id_server_url = url.parse(this.idBaseUrl);
|
const postParams = Object.assign({}, params);
|
||||||
if (id_server_url.host === null) {
|
|
||||||
|
// If the HS supports separate add and bind, then requestToken endpoints
|
||||||
|
// don't need an IS as they are all validated by the HS directly.
|
||||||
|
if (!await this.doesServerSupportSeparateAddAndBind() && this.idBaseUrl) {
|
||||||
|
const idServerUrl = url.parse(this.idBaseUrl);
|
||||||
|
if (!idServerUrl.host) {
|
||||||
throw new Error("Invalid ID server URL: " + this.idBaseUrl);
|
throw new Error("Invalid ID server URL: " + this.idBaseUrl);
|
||||||
}
|
}
|
||||||
|
postParams.id_server = idServerUrl.host;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.identityServer &&
|
||||||
|
this.identityServer.getAccessToken &&
|
||||||
|
await this.doesServerAcceptIdentityAccessToken()
|
||||||
|
) {
|
||||||
|
const identityAccessToken = await this.identityServer.getAccessToken();
|
||||||
|
if (identityAccessToken) {
|
||||||
|
postParams.id_access_token = identityAccessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const postParams = Object.assign({}, params, {
|
|
||||||
id_server: id_server_url.host,
|
|
||||||
});
|
|
||||||
return this._http.request(
|
return this._http.request(
|
||||||
undefined, "POST", endpoint, undefined,
|
undefined, "POST", endpoint, undefined,
|
||||||
postParams,
|
postParams,
|
||||||
@@ -3959,6 +4168,77 @@ MatrixClient.prototype.getTurnServers = function() {
|
|||||||
return this._turnServers || [];
|
return this._turnServers || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to allow a fallback ICE server should be used for negotiating a
|
||||||
|
* WebRTC connection if the homeserver doesn't provide any servers. Defaults to
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @param {boolean} allow
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.setFallbackICEServerAllowed = function(allow) {
|
||||||
|
this._fallbackICEServerAllowed = allow;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether to allow a fallback ICE server should be used for negotiating a
|
||||||
|
* WebRTC connection if the homeserver doesn't provide any servers. Defaults to
|
||||||
|
* false.
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.isFallbackICEServerAllowed = function() {
|
||||||
|
return this._fallbackICEServerAllowed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Synapse-specific APIs
|
||||||
|
// =====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the current user is an administrator of the Synapse homeserver.
|
||||||
|
* Returns false if untrue or the homeserver does not appear to be a Synapse
|
||||||
|
* homeserver. <strong>This function is implementation specific and may change
|
||||||
|
* as a result.</strong>
|
||||||
|
* @return {boolean} true if the user appears to be a Synapse administrator.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.isSynapseAdministrator = function() {
|
||||||
|
return this.whoisSynapseUser(this.getUserId())
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a whois lookup on a user using Synapse's administrator API.
|
||||||
|
* <strong>This function is implementation specific and may change as a
|
||||||
|
* result.</strong>
|
||||||
|
* @param {string} userId the User ID to look up.
|
||||||
|
* @return {object} the whois response - see Synapse docs for information.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.whoisSynapseUser = function(userId) {
|
||||||
|
const path = utils.encodeUri(
|
||||||
|
"/_synapse/admin/v1/whois/$userId",
|
||||||
|
{ $userId: userId },
|
||||||
|
);
|
||||||
|
return this._http.authedRequest(
|
||||||
|
undefined, 'GET', path, undefined, undefined, {prefix: ''},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivates a user using Synapse's administrator API. <strong>This
|
||||||
|
* function is implementation specific and may change as a result.</strong>
|
||||||
|
* @param {string} userId the User ID to deactivate.
|
||||||
|
* @return {object} the deactivate response - see Synapse docs for information.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.deactivateSynapseUser = function(userId) {
|
||||||
|
const path = utils.encodeUri(
|
||||||
|
"/_synapse/admin/v1/deactivate/$userId",
|
||||||
|
{ $userId: userId },
|
||||||
|
);
|
||||||
|
return this._http.authedRequest(
|
||||||
|
undefined, 'POST', path, undefined, undefined, {prefix: ''},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// Higher level APIs
|
// Higher level APIs
|
||||||
// =================
|
// =================
|
||||||
|
|
||||||
@@ -4090,13 +4370,14 @@ MatrixClient.prototype.stopClient = function() {
|
|||||||
global.clearTimeout(this._checkTurnServersTimeoutID);
|
global.clearTimeout(this._checkTurnServersTimeoutID);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Query the server to see if it support members lazy loading
|
* Get the API versions supported by the server, along with any
|
||||||
* @return {Promise<boolean>} true if server supports lazy loading
|
* unstable APIs it supports
|
||||||
|
* @return {Promise<object>} The server /versions response
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.doesServerSupportLazyLoading = async function() {
|
MatrixClient.prototype.getVersions = async function() {
|
||||||
if (this._serverSupportsLazyLoading === null) {
|
if (this._serverVersionsCache === null) {
|
||||||
const response = await this._http.request(
|
this._serverVersionsCache = await this._http.request(
|
||||||
undefined, // callback
|
undefined, // callback
|
||||||
"GET", "/_matrix/client/versions",
|
"GET", "/_matrix/client/versions",
|
||||||
undefined, // queryParams
|
undefined, // queryParams
|
||||||
@@ -4105,14 +4386,80 @@ MatrixClient.prototype.doesServerSupportLazyLoading = async function() {
|
|||||||
prefix: '',
|
prefix: '',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const unstableFeatures = response["unstable_features"];
|
|
||||||
this._serverSupportsLazyLoading =
|
|
||||||
unstableFeatures && unstableFeatures["m.lazy_load_members"];
|
|
||||||
}
|
}
|
||||||
return this._serverSupportsLazyLoading;
|
return this._serverVersionsCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* Query the server to see if it support members lazy loading
|
||||||
|
* @return {Promise<boolean>} true if server supports lazy loading
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.doesServerSupportLazyLoading = async function() {
|
||||||
|
const response = await this.getVersions();
|
||||||
|
|
||||||
|
const versions = response["versions"];
|
||||||
|
const unstableFeatures = response["unstable_features"];
|
||||||
|
|
||||||
|
return (versions && versions.includes("r0.5.0"))
|
||||||
|
|| (unstableFeatures && unstableFeatures["m.lazy_load_members"]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the server to see if the `id_server` parameter is required
|
||||||
|
* when registering with an 3pid, adding a 3pid or resetting password.
|
||||||
|
* @return {Promise<boolean>} true if id_server parameter is required
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.doesServerRequireIdServerParam = async function() {
|
||||||
|
const response = await this.getVersions();
|
||||||
|
|
||||||
|
const versions = response["versions"];
|
||||||
|
|
||||||
|
// Supporting r0.6.0 is the same as having the flag set to false
|
||||||
|
if (versions && versions.includes("r0.6.0")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unstableFeatures = response["unstable_features"];
|
||||||
|
if (unstableFeatures["m.require_identity_server"] === undefined) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return unstableFeatures["m.require_identity_server"];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the server to see if the `id_access_token` parameter can be safely
|
||||||
|
* passed to the homeserver. Some homeservers may trigger errors if they are not
|
||||||
|
* prepared for the new parameter.
|
||||||
|
* @return {Promise<boolean>} true if id_access_token can be sent
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.doesServerAcceptIdentityAccessToken = async function() {
|
||||||
|
const response = await this.getVersions();
|
||||||
|
|
||||||
|
const versions = response["versions"];
|
||||||
|
const unstableFeatures = response["unstable_features"];
|
||||||
|
|
||||||
|
return (versions && versions.includes("r0.6.0"))
|
||||||
|
|| (unstableFeatures && unstableFeatures["m.id_access_token"]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the server to see if it supports separate 3PID add and bind functions.
|
||||||
|
* This affects the sequence of API calls clients should use for these operations,
|
||||||
|
* so it's helpful to be able to check for support.
|
||||||
|
* @return {Promise<boolean>} true if separate functions are supported
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.doesServerSupportSeparateAddAndBind = async function() {
|
||||||
|
const response = await this.getVersions();
|
||||||
|
|
||||||
|
const versions = response["versions"];
|
||||||
|
const unstableFeatures = response["unstable_features"];
|
||||||
|
|
||||||
|
return (versions && versions.includes("r0.6.0"))
|
||||||
|
|| (unstableFeatures && unstableFeatures["m.separate_add_and_bind"]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
* Get if lazy loading members is being used.
|
* Get if lazy loading members is being used.
|
||||||
* @return {boolean} Whether or not members are lazy loaded by this client
|
* @return {boolean} Whether or not members are lazy loaded by this client
|
||||||
*/
|
*/
|
||||||
@@ -4120,7 +4467,7 @@ MatrixClient.prototype.hasLazyLoadMembersEnabled = function() {
|
|||||||
return !!this._clientOpts.lazyLoadMembers;
|
return !!this._clientOpts.lazyLoadMembers;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Set a function which is called when /sync returns a 'limited' response.
|
* Set a function which is called when /sync returns a 'limited' response.
|
||||||
* It is called with a room ID and returns a boolean. It should return 'true' if the SDK
|
* It is called with a room ID and returns a boolean. It should return 'true' if the SDK
|
||||||
* can SAFELY remove events from this room. It may not be safe to remove events if there
|
* can SAFELY remove events from this room. It may not be safe to remove events if there
|
||||||
@@ -4141,6 +4488,47 @@ MatrixClient.prototype.getCanResetTimelineCallback = function() {
|
|||||||
return this._canResetTimelineCallback;
|
return this._canResetTimelineCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns relations for a given event. Handles encryption transparently,
|
||||||
|
* with the caveat that the amount of events returned might be 0, even though you get a nextBatch.
|
||||||
|
* When the returned promise resolves, all messages should have finished trying to decrypt.
|
||||||
|
* @param {string} roomId the room of the event
|
||||||
|
* @param {string} eventId the id of the event
|
||||||
|
* @param {string} relationType the rel_type of the relations requested
|
||||||
|
* @param {string} eventType the event type of the relations requested
|
||||||
|
* @param {Object} opts options with optional values for the request.
|
||||||
|
* @param {Object} opts.from the pagination token returned from a previous request as `nextBatch` to return following relations.
|
||||||
|
* @return {Object} an object with `events` as `MatrixEvent[]` and optionally `nextBatch` if more relations are available.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.relations =
|
||||||
|
async function(roomId, eventId, relationType, eventType, opts = {}) {
|
||||||
|
const fetchedEventType = _getEncryptedIfNeededEventType(this, roomId, eventType);
|
||||||
|
const result = await this.fetchRelations(
|
||||||
|
roomId,
|
||||||
|
eventId,
|
||||||
|
relationType,
|
||||||
|
fetchedEventType,
|
||||||
|
opts);
|
||||||
|
const mapper = this.getEventMapper();
|
||||||
|
let originalEvent;
|
||||||
|
if (result.original_event) {
|
||||||
|
originalEvent = mapper(result.original_event);
|
||||||
|
}
|
||||||
|
let events = result.chunk.map(mapper);
|
||||||
|
if (fetchedEventType === "m.room.encrypted") {
|
||||||
|
const allEvents = originalEvent ? events.concat(originalEvent) : events;
|
||||||
|
await Promise.all(allEvents.map(e => {
|
||||||
|
return new Promise(resolve => e.once("Event.decrypted", resolve));
|
||||||
|
}));
|
||||||
|
events = events.filter(e => e.getType() === eventType);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
originalEvent,
|
||||||
|
events,
|
||||||
|
nextBatch: result.next_batch,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
function setupCallEventHandler(client) {
|
function setupCallEventHandler(client) {
|
||||||
const candidatesByCall = {
|
const candidatesByCall = {
|
||||||
// callId: [Candidate]
|
// callId: [Candidate]
|
||||||
@@ -4364,10 +4752,9 @@ function checkTurnServers(client) {
|
|||||||
}
|
}
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
logger.error("Failed to get TURN URIs");
|
logger.error("Failed to get TURN URIs");
|
||||||
client._checkTurnServersTimeoutID =
|
client._checkTurnServersTimeoutID = setTimeout(function() {
|
||||||
setTimeout(function() {
|
|
||||||
checkTurnServers(client);
|
checkTurnServers(client);
|
||||||
}, 60000);
|
}, 60000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4627,7 +5014,7 @@ module.exports.CRYPTO_ENABLED = CRYPTO_ENABLED;
|
|||||||
* when then login session can be renewed by using a refresh token.
|
* when then login session can be renewed by using a refresh token.
|
||||||
* @event module:client~MatrixClient#"Session.logged_out"
|
* @event module:client~MatrixClient#"Session.logged_out"
|
||||||
* @example
|
* @example
|
||||||
* matrixClient.on("Session.logged_out", function(call){
|
* matrixClient.on("Session.logged_out", function(errorObj){
|
||||||
* // show the login screen
|
* // show the login screen
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let serverAndMediaId = mxc.slice(6); // strips mxc://
|
let serverAndMediaId = mxc.slice(6); // strips mxc://
|
||||||
let prefix = "/_matrix/media/v1/download/";
|
let prefix = "/_matrix/media/r0/download/";
|
||||||
const params = {};
|
const params = {};
|
||||||
|
|
||||||
if (width) {
|
if (width) {
|
||||||
@@ -62,7 +62,7 @@ module.exports = {
|
|||||||
if (utils.keys(params).length > 0) {
|
if (utils.keys(params).length > 0) {
|
||||||
// these are thumbnailing params so they probably want the
|
// these are thumbnailing params so they probably want the
|
||||||
// thumbnailing API...
|
// thumbnailing API...
|
||||||
prefix = "/_matrix/media/v1/thumbnail/";
|
prefix = "/_matrix/media/r0/thumbnail/";
|
||||||
}
|
}
|
||||||
|
|
||||||
const fragmentOffset = serverAndMediaId.indexOf("#");
|
const fragmentOffset = serverAndMediaId.indexOf("#");
|
||||||
@@ -83,6 +83,7 @@ module.exports = {
|
|||||||
* @param {Number} width The desired width of the image in pixels. Default: 96.
|
* @param {Number} width The desired width of the image in pixels. Default: 96.
|
||||||
* @param {Number} height The desired height of the image in pixels. Default: 96.
|
* @param {Number} height The desired height of the image in pixels. Default: 96.
|
||||||
* @return {string} The complete URL to the identicon.
|
* @return {string} The complete URL to the identicon.
|
||||||
|
* @deprecated This is no longer in the specification.
|
||||||
*/
|
*/
|
||||||
getIdenticonUri: function(baseUrl, identiconString, width, height) {
|
getIdenticonUri: function(baseUrl, identiconString, width, height) {
|
||||||
if (!identiconString) {
|
if (!identiconString) {
|
||||||
@@ -99,7 +100,7 @@ module.exports = {
|
|||||||
height: height,
|
height: height,
|
||||||
};
|
};
|
||||||
|
|
||||||
const path = utils.encodeUri("/_matrix/media/v1/identicon/$ident", {
|
const path = utils.encodeUri("/_matrix/media/unstable/identicon/$ident", {
|
||||||
$ident: identiconString,
|
$ident: identiconString,
|
||||||
});
|
});
|
||||||
return baseUrl + path +
|
return baseUrl + path +
|
||||||
|
|||||||
@@ -594,6 +594,11 @@ OlmDevice.prototype.encryptMessage = async function(
|
|||||||
'readwrite', [IndexedDBCryptoStore.STORE_SESSIONS],
|
'readwrite', [IndexedDBCryptoStore.STORE_SESSIONS],
|
||||||
(txn) => {
|
(txn) => {
|
||||||
this._getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => {
|
this._getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => {
|
||||||
|
const sessionDesc = sessionInfo.session.describe();
|
||||||
|
console.log(
|
||||||
|
"Session ID " + sessionId + " to " +
|
||||||
|
theirDeviceIdentityKey + ": " + sessionDesc,
|
||||||
|
);
|
||||||
res = sessionInfo.session.encrypt(payloadString);
|
res = sessionInfo.session.encrypt(payloadString);
|
||||||
this._saveSession(theirDeviceIdentityKey, sessionInfo, txn);
|
this._saveSession(theirDeviceIdentityKey, sessionInfo, txn);
|
||||||
});
|
});
|
||||||
@@ -621,6 +626,11 @@ OlmDevice.prototype.decryptMessage = async function(
|
|||||||
'readwrite', [IndexedDBCryptoStore.STORE_SESSIONS],
|
'readwrite', [IndexedDBCryptoStore.STORE_SESSIONS],
|
||||||
(txn) => {
|
(txn) => {
|
||||||
this._getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => {
|
this._getSession(theirDeviceIdentityKey, sessionId, txn, (sessionInfo) => {
|
||||||
|
const sessionDesc = sessionInfo.session.describe();
|
||||||
|
console.log(
|
||||||
|
"Session ID " + sessionId + " to " +
|
||||||
|
theirDeviceIdentityKey + ": " + sessionDesc,
|
||||||
|
);
|
||||||
payloadString = sessionInfo.session.decrypt(messageType, ciphertext);
|
payloadString = sessionInfo.session.decrypt(messageType, ciphertext);
|
||||||
sessionInfo.lastReceivedMessageTs = Date.now();
|
sessionInfo.lastReceivedMessageTs = Date.now();
|
||||||
this._saveSession(theirDeviceIdentityKey, sessionInfo, txn);
|
this._saveSession(theirDeviceIdentityKey, sessionInfo, txn);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import logger from '../../../src/logger';
|
import logger from '../../logger';
|
||||||
|
|
||||||
const utils = require("../../utils");
|
const utils = require("../../utils");
|
||||||
const olmlib = require("../olmlib");
|
const olmlib = require("../olmlib");
|
||||||
|
|||||||
@@ -1224,6 +1224,132 @@ Crypto.prototype.setDeviceVerification = async function(
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function verificationEventHandler(target, userId, roomId, eventId) {
|
||||||
|
return function(event) {
|
||||||
|
// listen for events related to this verification
|
||||||
|
if (event.getRoomId() !== roomId
|
||||||
|
|| event.getSender() !== userId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const content = event.getContent();
|
||||||
|
if (!content["m.relates_to"]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const relatesTo
|
||||||
|
= content["m.relationship"] || content["m.relates_to"];
|
||||||
|
if (!relatesTo.rel_type
|
||||||
|
|| relatesTo.rel_type !== "m.reference"
|
||||||
|
|| !relatesTo.event_id
|
||||||
|
|| relatesTo.event_id !== eventId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the event seems to be related to this verification, so pass it on to
|
||||||
|
// the verification handler
|
||||||
|
target.handleEvent(event);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Crypto.prototype.requestVerificationDM = async function(userId, roomId, methods) {
|
||||||
|
let methodMap;
|
||||||
|
if (methods) {
|
||||||
|
methodMap = new Map();
|
||||||
|
for (const method of methods) {
|
||||||
|
if (typeof method === "string") {
|
||||||
|
methodMap.set(method, defaultVerificationMethods[method]);
|
||||||
|
} else if (method.NAME) {
|
||||||
|
methodMap.set(method.NAME, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
methodMap = this._baseApis._crypto._verificationMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
let eventId = undefined;
|
||||||
|
const listenPromise = new Promise((_resolve, _reject) => {
|
||||||
|
const listener = (event) => {
|
||||||
|
// listen for events related to this verification
|
||||||
|
if (event.getRoomId() !== roomId
|
||||||
|
|| event.getSender() !== userId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const relatesTo = event.getRelation();
|
||||||
|
if (!relatesTo || !relatesTo.rel_type
|
||||||
|
|| relatesTo.rel_type !== "m.reference"
|
||||||
|
|| !relatesTo.event_id
|
||||||
|
|| relatesTo.event_id !== eventId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = event.getContent();
|
||||||
|
// the event seems to be related to this verification
|
||||||
|
switch (event.getType()) {
|
||||||
|
case "m.key.verification.start": {
|
||||||
|
const verifier = new (methodMap.get(content.method))(
|
||||||
|
this._baseApis, userId, content.from_device, eventId,
|
||||||
|
roomId, event,
|
||||||
|
);
|
||||||
|
verifier.handler = verificationEventHandler(
|
||||||
|
verifier, userId, roomId, eventId,
|
||||||
|
);
|
||||||
|
// this handler gets removed when the verification finishes
|
||||||
|
// (see the verify method of crypto/verification/Base.js)
|
||||||
|
this._baseApis.on("event", verifier.handler);
|
||||||
|
resolve(verifier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "m.key.verification.cancel": {
|
||||||
|
reject(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this._baseApis.on("event", listener);
|
||||||
|
|
||||||
|
const resolve = (...args) => {
|
||||||
|
this._baseApis.off("event", listener);
|
||||||
|
_resolve(...args);
|
||||||
|
};
|
||||||
|
const reject = (...args) => {
|
||||||
|
this._baseApis.off("event", listener);
|
||||||
|
_reject(...args);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await this._baseApis.sendEvent(
|
||||||
|
roomId, "m.room.message",
|
||||||
|
{
|
||||||
|
body: this._baseApis.getUserId() + " is requesting to verify " +
|
||||||
|
"your key, but your client does not support in-chat key " +
|
||||||
|
"verification. You will need to use legacy key " +
|
||||||
|
"verification to verify keys.",
|
||||||
|
msgtype: "m.key.verification.request",
|
||||||
|
to: userId,
|
||||||
|
from_device: this._baseApis.getDeviceId(),
|
||||||
|
methods: [...methodMap.keys()],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
eventId = res.event_id;
|
||||||
|
|
||||||
|
return listenPromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
Crypto.prototype.acceptVerificationDM = function(event, Method) {
|
||||||
|
if (typeof(Method) === "string") {
|
||||||
|
Method = defaultVerificationMethods[Method];
|
||||||
|
}
|
||||||
|
const content = event.getContent();
|
||||||
|
const verifier = new Method(
|
||||||
|
this._baseApis, event.getSender(), content.from_device, event.getId(),
|
||||||
|
event.getRoomId(),
|
||||||
|
);
|
||||||
|
verifier.handler = verificationEventHandler(
|
||||||
|
verifier, event.getSender(), event.getRoomId(), event.getId(),
|
||||||
|
);
|
||||||
|
this._baseApis.on("event", verifier.handler);
|
||||||
|
return verifier;
|
||||||
|
};
|
||||||
|
|
||||||
Crypto.prototype.requestVerification = function(userId, methods, devices) {
|
Crypto.prototype.requestVerification = function(userId, methods, devices) {
|
||||||
if (!methods) {
|
if (!methods) {
|
||||||
// .keys() returns an iterator, so we need to explicitly turn it into an array
|
// .keys() returns an iterator, so we need to explicitly turn it into an array
|
||||||
@@ -1271,20 +1397,7 @@ Crypto.prototype.beginKeyVerification = function(
|
|||||||
this._verificationTransactions.set(userId, new Map());
|
this._verificationTransactions.set(userId, new Map());
|
||||||
}
|
}
|
||||||
transactionId = transactionId || randomString(32);
|
transactionId = transactionId || randomString(32);
|
||||||
if (method instanceof Array) {
|
if (this._verificationMethods.has(method)) {
|
||||||
if (method.length !== 2
|
|
||||||
|| !this._verificationMethods.has(method[0])
|
|
||||||
|| !this._verificationMethods.has(method[1])) {
|
|
||||||
throw newUnknownMethodError();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
return new TwoPartVerification(
|
|
||||||
this._verificationMethods[method[0]],
|
|
||||||
this._verificationMethods[method[1]],
|
|
||||||
userId, deviceId, transactionId,
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
} else if (this._verificationMethods.has(method)) {
|
|
||||||
const verifier = new (this._verificationMethods.get(method))(
|
const verifier = new (this._verificationMethods.get(method))(
|
||||||
this._baseApis, userId, deviceId, transactionId,
|
this._baseApis, userId, deviceId, transactionId,
|
||||||
);
|
);
|
||||||
@@ -1419,6 +1532,15 @@ Crypto.prototype.forceDiscardSession = function(roomId) {
|
|||||||
* the device query is always inhibited as the members are not tracked.
|
* the device query is always inhibited as the members are not tracked.
|
||||||
*/
|
*/
|
||||||
Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDeviceQuery) {
|
Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDeviceQuery) {
|
||||||
|
// ignore crypto events with no algorithm defined
|
||||||
|
// This will happen if a crypto event is redacted before we fetch the room state
|
||||||
|
// It would otherwise just throw later as an unknown algorithm would, but we may
|
||||||
|
// as well catch this here
|
||||||
|
if (!config.algorithm) {
|
||||||
|
console.log("Ignoring setRoomEncryption with no algorithm");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if state is being replayed from storage, we might already have a configuration
|
// if state is being replayed from storage, we might already have a configuration
|
||||||
// for this room as they are persisted as well.
|
// for this room as they are persisted as well.
|
||||||
// We just need to make sure the algorithm is initialized in this case.
|
// We just need to make sure the algorithm is initialized in this case.
|
||||||
@@ -1756,6 +1878,18 @@ Crypto.prototype.backupGroupSession = async function(
|
|||||||
* upload in the background as soon as possible.
|
* upload in the background as soon as possible.
|
||||||
*/
|
*/
|
||||||
Crypto.prototype.scheduleAllGroupSessionsForBackup = async function() {
|
Crypto.prototype.scheduleAllGroupSessionsForBackup = async function() {
|
||||||
|
await this.flagAllGroupSessionsForBackup();
|
||||||
|
|
||||||
|
// Schedule keys to upload in the background as soon as possible.
|
||||||
|
this.scheduleKeyBackupSend(0 /* maxDelay */);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks all group sessions as needing to be backed up without scheduling
|
||||||
|
* them to upload in the background.
|
||||||
|
* @returns {Promise<int>} Resolves to the number of sessions requiring a backup.
|
||||||
|
*/
|
||||||
|
Crypto.prototype.flagAllGroupSessionsForBackup = async function() {
|
||||||
await this._cryptoStore.doTxn(
|
await this._cryptoStore.doTxn(
|
||||||
'readwrite',
|
'readwrite',
|
||||||
[
|
[
|
||||||
@@ -1773,9 +1907,7 @@ Crypto.prototype.scheduleAllGroupSessionsForBackup = async function() {
|
|||||||
|
|
||||||
const remaining = await this._cryptoStore.countSessionsNeedingBackup();
|
const remaining = await this._cryptoStore.countSessionsNeedingBackup();
|
||||||
this.emit("crypto.keyBackupSessionsRemaining", remaining);
|
this.emit("crypto.keyBackupSessionsRemaining", remaining);
|
||||||
|
return remaining;
|
||||||
// Schedule keys to upload in the background as soon as possible.
|
|
||||||
this.scheduleKeyBackupSend(0 /* maxDelay */);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307
|
/* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307
|
||||||
@@ -2122,6 +2254,11 @@ Crypto.prototype._onRoomKeyEvent = function(event) {
|
|||||||
* @param {module:models/event.MatrixEvent} event verification request event
|
* @param {module:models/event.MatrixEvent} event verification request event
|
||||||
*/
|
*/
|
||||||
Crypto.prototype._onKeyVerificationRequest = function(event) {
|
Crypto.prototype._onKeyVerificationRequest = function(event) {
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
logger.warn("Ignoring flagged verification request from " + event.getSender());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
if (!("from_device" in content) || typeof content.from_device !== "string"
|
if (!("from_device" in content) || typeof content.from_device !== "string"
|
||||||
|| !("transaction_id" in content) || typeof content.from_device !== "string"
|
|| !("transaction_id" in content) || typeof content.from_device !== "string"
|
||||||
@@ -2238,6 +2375,11 @@ Crypto.prototype._onKeyVerificationRequest = function(event) {
|
|||||||
* @param {module:models/event.MatrixEvent} event verification start event
|
* @param {module:models/event.MatrixEvent} event verification start event
|
||||||
*/
|
*/
|
||||||
Crypto.prototype._onKeyVerificationStart = function(event) {
|
Crypto.prototype._onKeyVerificationStart = function(event) {
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
logger.warn("Ignoring flagged verification start from " + event.getSender());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const sender = event.getSender();
|
const sender = event.getSender();
|
||||||
const content = event.getContent();
|
const content = event.getContent();
|
||||||
const transactionId = content.transaction_id;
|
const transactionId = content.transaction_id;
|
||||||
@@ -2271,22 +2413,6 @@ Crypto.prototype._onKeyVerificationStart = function(event) {
|
|||||||
transaction_id: content.transactionId,
|
transaction_id: content.transactionId,
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
} else if (content.next_method) {
|
|
||||||
if (!this._verificationMethods.has(content.next_method)) {
|
|
||||||
cancel(newUnknownMethodError({
|
|
||||||
transaction_id: content.transactionId,
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
/* TODO:
|
|
||||||
const verification = new TwoPartVerification(
|
|
||||||
this._verificationMethods[content.method],
|
|
||||||
this._verificationMethods[content.next_method],
|
|
||||||
userId, deviceId,
|
|
||||||
);
|
|
||||||
this.emit(verification.event_type, verification);
|
|
||||||
this.emit(verification.first.event_type, verification);*/
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const verifier = new (this._verificationMethods.get(content.method))(
|
const verifier = new (this._verificationMethods.get(content.method))(
|
||||||
this._baseApis, sender, deviceId, content.transaction_id,
|
this._baseApis, sender, deviceId, content.transaction_id,
|
||||||
@@ -2341,8 +2467,6 @@ Crypto.prototype._onKeyVerificationStart = function(event) {
|
|||||||
|
|
||||||
handler.request.resolve(verifier);
|
handler.request.resolve(verifier);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// FIXME: make sure we're in a two-part verification, and the start matches the second part
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._baseApis.emit("crypto.verification.start", verifier);
|
this._baseApis.emit("crypto.verification.start", verifier);
|
||||||
|
|||||||
@@ -719,7 +719,7 @@ function promiseifyTxn(txn) {
|
|||||||
if (txn._mx_abortexception !== undefined) {
|
if (txn._mx_abortexception !== undefined) {
|
||||||
reject(txn._mx_abortexception);
|
reject(txn._mx_abortexception);
|
||||||
} else {
|
} else {
|
||||||
localStorage.log("Error performing indexeddb txn", event);
|
logger.log("Error performing indexeddb txn", event);
|
||||||
reject(event.target.error);
|
reject(event.target.error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
import logger from '../../logger';
|
import logger from '../../logger';
|
||||||
import MemoryCryptoStore from './memory-crypto-store.js';
|
import MemoryCryptoStore from './memory-crypto-store';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal module. Partial localStorage backed storage for e2e.
|
* Internal module. Partial localStorage backed storage for e2e.
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import {MatrixEvent} from '../../models/event';
|
|||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import logger from '../../logger';
|
import logger from '../../logger';
|
||||||
import DeviceInfo from '../deviceinfo';
|
import DeviceInfo from '../deviceinfo';
|
||||||
|
import {newTimeoutError} from "./Error";
|
||||||
|
|
||||||
|
const timeoutException = new Error("Verification timed out");
|
||||||
|
|
||||||
export default class VerificationBase extends EventEmitter {
|
export default class VerificationBase extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
@@ -45,27 +48,66 @@ export default class VerificationBase extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @param {string} transactionId the transaction ID to be used when sending events
|
* @param {string} transactionId the transaction ID to be used when sending events
|
||||||
*
|
*
|
||||||
* @param {object} startEvent the m.key.verification.start event that
|
* @param {string} [roomId] the room to use for verification
|
||||||
|
*
|
||||||
|
* @param {object} [startEvent] the m.key.verification.start event that
|
||||||
* initiated this verification, if any
|
* initiated this verification, if any
|
||||||
*
|
*
|
||||||
* @param {object} request the key verification request object related to
|
* @param {object} [request] the key verification request object related to
|
||||||
* this verification, if any
|
* this verification, if any
|
||||||
*
|
|
||||||
* @param {object} parent parent verification for this verification, if any
|
|
||||||
*/
|
*/
|
||||||
constructor(baseApis, userId, deviceId, transactionId, startEvent, request, parent) {
|
constructor(baseApis, userId, deviceId, transactionId, roomId, startEvent, request) {
|
||||||
super();
|
super();
|
||||||
this._baseApis = baseApis;
|
this._baseApis = baseApis;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.transactionId = transactionId;
|
this.transactionId = transactionId;
|
||||||
|
if (typeof(roomId) === "string" || roomId instanceof String) {
|
||||||
|
this.roomId = roomId;
|
||||||
this.startEvent = startEvent;
|
this.startEvent = startEvent;
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this._parent = parent;
|
} else {
|
||||||
|
// if room ID was omitted, but start event and request were not
|
||||||
|
this.startEvent= roomId;
|
||||||
|
this.request = startEvent;
|
||||||
|
}
|
||||||
|
this.cancelled = false;
|
||||||
this._done = false;
|
this._done = false;
|
||||||
this._promise = null;
|
this._promise = null;
|
||||||
|
this._transactionTimeoutTimer = null;
|
||||||
|
|
||||||
|
// At this point, the verification request was received so start the timeout timer.
|
||||||
|
this._resetTimer();
|
||||||
|
|
||||||
|
if (this.roomId) {
|
||||||
|
this._send = this._sendMessage;
|
||||||
|
} else {
|
||||||
|
this._send = this._sendToDevice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_resetTimer() {
|
||||||
|
logger.info("Refreshing/starting the verification transaction timeout timer");
|
||||||
|
if (this._transactionTimeoutTimer !== null) {
|
||||||
|
clearTimeout(this._transactionTimeoutTimer);
|
||||||
|
}
|
||||||
|
this._transactionTimeoutTimer = setTimeout(() => {
|
||||||
|
if (!this._done && !this.cancelled) {
|
||||||
|
logger.info("Triggering verification timeout");
|
||||||
|
this.cancel(timeoutException);
|
||||||
|
}
|
||||||
|
}, 10 * 60 * 1000); // 10 minutes
|
||||||
|
}
|
||||||
|
|
||||||
|
_endTimer() {
|
||||||
|
if (this._transactionTimeoutTimer !== null) {
|
||||||
|
clearTimeout(this._transactionTimeoutTimer);
|
||||||
|
this._transactionTimeoutTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send a message to the other participant, using to-device messages
|
||||||
|
*/
|
||||||
_sendToDevice(type, content) {
|
_sendToDevice(type, content) {
|
||||||
if (this._done) {
|
if (this._done) {
|
||||||
return Promise.reject(new Error("Verification is already done"));
|
return Promise.reject(new Error("Verification is already done"));
|
||||||
@@ -76,6 +118,21 @@ export default class VerificationBase extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* send a message to the other participant, using in-roomm messages
|
||||||
|
*/
|
||||||
|
_sendMessage(type, content) {
|
||||||
|
if (this._done) {
|
||||||
|
return Promise.reject(new Error("Verification is already done"));
|
||||||
|
}
|
||||||
|
// FIXME: if MSC1849 decides to use m.relationship instead of
|
||||||
|
// m.relates_to, we should follow suit here
|
||||||
|
content["m.relates_to"] = {
|
||||||
|
rel_type: "m.reference",
|
||||||
|
event_id: this.transactionId,
|
||||||
|
};
|
||||||
|
return this._baseApis.sendEvent(this.roomId, type, content);
|
||||||
|
}
|
||||||
|
|
||||||
_waitForEvent(type) {
|
_waitForEvent(type) {
|
||||||
if (this._done) {
|
if (this._done) {
|
||||||
return Promise.reject(new Error("Verification is already done"));
|
return Promise.reject(new Error("Verification is already done"));
|
||||||
@@ -93,6 +150,7 @@ export default class VerificationBase extends EventEmitter {
|
|||||||
} else if (e.getType() === this._expectedEvent) {
|
} else if (e.getType() === this._expectedEvent) {
|
||||||
this._expectedEvent = undefined;
|
this._expectedEvent = undefined;
|
||||||
this._rejectEvent = undefined;
|
this._rejectEvent = undefined;
|
||||||
|
this._resetTimer();
|
||||||
this._resolveEvent(e);
|
this._resolveEvent(e);
|
||||||
} else {
|
} else {
|
||||||
this._expectedEvent = undefined;
|
this._expectedEvent = undefined;
|
||||||
@@ -110,17 +168,27 @@ export default class VerificationBase extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
done() {
|
done() {
|
||||||
|
this._endTimer(); // always kill the activity timer
|
||||||
if (!this._done) {
|
if (!this._done) {
|
||||||
|
if (this.roomId) {
|
||||||
|
// verification in DM requires a done message
|
||||||
|
this._send("m.key.verification.done", {});
|
||||||
|
}
|
||||||
this._resolve();
|
this._resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(e) {
|
cancel(e) {
|
||||||
|
this._endTimer(); // always kill the activity timer
|
||||||
if (!this._done) {
|
if (!this._done) {
|
||||||
|
this.cancelled = true;
|
||||||
if (this.userId && this.deviceId && this.transactionId) {
|
if (this.userId && this.deviceId && this.transactionId) {
|
||||||
// send a cancellation to the other user (if it wasn't
|
// send a cancellation to the other user (if it wasn't
|
||||||
// cancelled by the other user)
|
// cancelled by the other user)
|
||||||
if (e instanceof MatrixEvent) {
|
if (e === timeoutException) {
|
||||||
|
const timeoutEvent = newTimeoutError();
|
||||||
|
this._send(timeoutEvent.getType(), timeoutEvent.getContent());
|
||||||
|
} else if (e instanceof MatrixEvent) {
|
||||||
const sender = e.getSender();
|
const sender = e.getSender();
|
||||||
if (sender !== this.userId) {
|
if (sender !== this.userId) {
|
||||||
const content = e.getContent();
|
const content = e.getContent();
|
||||||
@@ -129,9 +197,9 @@ export default class VerificationBase extends EventEmitter {
|
|||||||
content.reason = content.reason || content.body
|
content.reason = content.reason || content.body
|
||||||
|| "Unknown reason";
|
|| "Unknown reason";
|
||||||
content.transaction_id = this.transactionId;
|
content.transaction_id = this.transactionId;
|
||||||
this._sendToDevice("m.key.verification.cancel", content);
|
this._send("m.key.verification.cancel", content);
|
||||||
} else {
|
} else {
|
||||||
this._sendToDevice("m.key.verification.cancel", {
|
this._send("m.key.verification.cancel", {
|
||||||
code: "m.unknown",
|
code: "m.unknown",
|
||||||
reason: content.body || "Unknown reason",
|
reason: content.body || "Unknown reason",
|
||||||
transaction_id: this.transactionId,
|
transaction_id: this.transactionId,
|
||||||
@@ -139,7 +207,7 @@ export default class VerificationBase extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._sendToDevice("m.key.verification.cancel", {
|
this._send("m.key.verification.cancel", {
|
||||||
code: "m.unknown",
|
code: "m.unknown",
|
||||||
reason: e.toString(),
|
reason: e.toString(),
|
||||||
transaction_id: this.transactionId,
|
transaction_id: this.transactionId,
|
||||||
@@ -147,7 +215,9 @@ export default class VerificationBase extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this._promise !== null) {
|
if (this._promise !== null) {
|
||||||
this._reject(e);
|
// when we cancel without a promise, we end up with a promise
|
||||||
|
// but no reject function. If cancel is called again, we'd error.
|
||||||
|
if (this._reject) this._reject(e);
|
||||||
} else {
|
} else {
|
||||||
this._promise = Promise.reject(e);
|
this._promise = Promise.reject(e);
|
||||||
}
|
}
|
||||||
@@ -169,15 +239,24 @@ export default class VerificationBase extends EventEmitter {
|
|||||||
this._promise = new Promise((resolve, reject) => {
|
this._promise = new Promise((resolve, reject) => {
|
||||||
this._resolve = (...args) => {
|
this._resolve = (...args) => {
|
||||||
this._done = true;
|
this._done = true;
|
||||||
|
this._endTimer();
|
||||||
|
if (this.handler) {
|
||||||
|
this._baseApis.off("event", this.handler);
|
||||||
|
}
|
||||||
resolve(...args);
|
resolve(...args);
|
||||||
};
|
};
|
||||||
this._reject = (...args) => {
|
this._reject = (...args) => {
|
||||||
this._done = true;
|
this._done = true;
|
||||||
|
this._endTimer();
|
||||||
|
if (this.handler) {
|
||||||
|
this._baseApis.off("event", this.handler);
|
||||||
|
}
|
||||||
reject(...args);
|
reject(...args);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
if (this._doVerification && !this._started) {
|
if (this._doVerification && !this._started) {
|
||||||
this._started = true;
|
this._started = true;
|
||||||
|
this._resetTimer(); // restart the timeout
|
||||||
Promise.resolve(this._doVerification())
|
Promise.resolve(this._doVerification())
|
||||||
.then(this.done.bind(this), this.cancel.bind(this));
|
.then(this.done.bind(this), this.cancel.bind(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,9 +213,10 @@ export default class SAS extends Base {
|
|||||||
message_authentication_codes: MAC_LIST,
|
message_authentication_codes: MAC_LIST,
|
||||||
// FIXME: allow app to specify what SAS methods can be used
|
// FIXME: allow app to specify what SAS methods can be used
|
||||||
short_authentication_string: SAS_LIST,
|
short_authentication_string: SAS_LIST,
|
||||||
transaction_id: this.transactionId,
|
|
||||||
};
|
};
|
||||||
this._sendToDevice("m.key.verification.start", initialMessage);
|
// NOTE: this._send will modify initialMessage to include the
|
||||||
|
// transaction_id field, or the m.relationship/m.relates_to field
|
||||||
|
this._send("m.key.verification.start", initialMessage);
|
||||||
|
|
||||||
|
|
||||||
let e = await this._waitForEvent("m.key.verification.accept");
|
let e = await this._waitForEvent("m.key.verification.accept");
|
||||||
@@ -235,7 +236,7 @@ export default class SAS extends Base {
|
|||||||
const hashCommitment = content.commitment;
|
const hashCommitment = content.commitment;
|
||||||
const olmSAS = new global.Olm.SAS();
|
const olmSAS = new global.Olm.SAS();
|
||||||
try {
|
try {
|
||||||
this._sendToDevice("m.key.verification.key", {
|
this._send("m.key.verification.key", {
|
||||||
key: olmSAS.get_pubkey(),
|
key: olmSAS.get_pubkey(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -306,7 +307,7 @@ export default class SAS extends Base {
|
|||||||
const olmSAS = new global.Olm.SAS();
|
const olmSAS = new global.Olm.SAS();
|
||||||
try {
|
try {
|
||||||
const commitmentStr = olmSAS.get_pubkey() + anotherjson.stringify(content);
|
const commitmentStr = olmSAS.get_pubkey() + anotherjson.stringify(content);
|
||||||
this._sendToDevice("m.key.verification.accept", {
|
this._send("m.key.verification.accept", {
|
||||||
key_agreement_protocol: keyAgreement,
|
key_agreement_protocol: keyAgreement,
|
||||||
hash: hashMethod,
|
hash: hashMethod,
|
||||||
message_authentication_code: macMethod,
|
message_authentication_code: macMethod,
|
||||||
@@ -320,7 +321,7 @@ export default class SAS extends Base {
|
|||||||
// FIXME: make sure event is properly formed
|
// FIXME: make sure event is properly formed
|
||||||
content = e.getContent();
|
content = e.getContent();
|
||||||
olmSAS.set_their_key(content.key);
|
olmSAS.set_their_key(content.key);
|
||||||
this._sendToDevice("m.key.verification.key", {
|
this._send("m.key.verification.key", {
|
||||||
key: olmSAS.get_pubkey(),
|
key: olmSAS.get_pubkey(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -382,7 +383,7 @@ export default class SAS extends Base {
|
|||||||
keyList.sort().join(","),
|
keyList.sort().join(","),
|
||||||
baseInfo + "KEY_IDS",
|
baseInfo + "KEY_IDS",
|
||||||
);
|
);
|
||||||
this._sendToDevice("m.key.verification.mac", { mac, keys });
|
this._send("m.key.verification.mac", { mac, keys });
|
||||||
}
|
}
|
||||||
|
|
||||||
async _checkMAC(olmSAS, content, method) {
|
async _checkMAC(olmSAS, content, method) {
|
||||||
|
|||||||
130
src/http-api.js
130
src/http-api.js
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -22,7 +23,7 @@ import Promise from 'bluebird';
|
|||||||
const parseContentType = require('content-type').parse;
|
const parseContentType = require('content-type').parse;
|
||||||
|
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
import logger from '../src/logger';
|
import logger from './logger';
|
||||||
|
|
||||||
// we use our own implementation of setTimeout, so that if we get suspended in
|
// we use our own implementation of setTimeout, so that if we get suspended in
|
||||||
// the middle of a /sync, we cancel the sync as soon as we awake, rather than
|
// the middle of a /sync, we cancel the sync as soon as we awake, rather than
|
||||||
@@ -46,10 +47,15 @@ module.exports.PREFIX_R0 = "/_matrix/client/r0";
|
|||||||
module.exports.PREFIX_UNSTABLE = "/_matrix/client/unstable";
|
module.exports.PREFIX_UNSTABLE = "/_matrix/client/unstable";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URI path for the identity API
|
* URI path for v1 of the the identity API
|
||||||
*/
|
*/
|
||||||
module.exports.PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1";
|
module.exports.PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URI path for the v2 identity API
|
||||||
|
*/
|
||||||
|
module.exports.PREFIX_IDENTITY_V2 = "/_matrix/identity/v2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URI path for the media repo API
|
* URI path for the media repo API
|
||||||
*/
|
*/
|
||||||
@@ -90,6 +96,13 @@ module.exports.MatrixHttpApi = function MatrixHttpApi(event_emitter, opts) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports.MatrixHttpApi.prototype = {
|
module.exports.MatrixHttpApi.prototype = {
|
||||||
|
/**
|
||||||
|
* Sets the baase URL for the identity server
|
||||||
|
* @param {string} url The new base url
|
||||||
|
*/
|
||||||
|
setIdBaseUrl: function(url) {
|
||||||
|
this.opts.idBaseUrl = url;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the content repository url with query parameters.
|
* Get the content repository url with query parameters.
|
||||||
@@ -102,7 +115,7 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
base: this.opts.baseUrl,
|
base: this.opts.baseUrl,
|
||||||
path: "/_matrix/media/v1/upload",
|
path: "/_matrix/media/r0/upload",
|
||||||
params: params,
|
params: params,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -291,7 +304,7 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let url = this.opts.baseUrl + "/_matrix/media/v1/upload";
|
let url = this.opts.baseUrl + "/_matrix/media/r0/upload";
|
||||||
|
|
||||||
const queryArgs = [];
|
const queryArgs = [];
|
||||||
|
|
||||||
@@ -327,7 +340,7 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
|
|
||||||
promise = this.authedRequest(
|
promise = this.authedRequest(
|
||||||
opts.callback, "POST", "/upload", queryParams, body, {
|
opts.callback, "POST", "/upload", queryParams, body, {
|
||||||
prefix: "/_matrix/media/v1",
|
prefix: "/_matrix/media/r0",
|
||||||
headers: {"Content-Type": contentType},
|
headers: {"Content-Type": contentType},
|
||||||
json: false,
|
json: false,
|
||||||
bodyParser: bodyParser,
|
bodyParser: bodyParser,
|
||||||
@@ -368,7 +381,18 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
return this.uploads;
|
return this.uploads;
|
||||||
},
|
},
|
||||||
|
|
||||||
idServerRequest: function(callback, method, path, params, prefix) {
|
idServerRequest: function(
|
||||||
|
callback,
|
||||||
|
method,
|
||||||
|
path,
|
||||||
|
params,
|
||||||
|
prefix,
|
||||||
|
accessToken,
|
||||||
|
) {
|
||||||
|
if (!this.opts.idBaseUrl) {
|
||||||
|
throw new Error("No Identity Server base URL set");
|
||||||
|
}
|
||||||
|
|
||||||
const fullUri = this.opts.idBaseUrl + prefix + path;
|
const fullUri = this.opts.idBaseUrl + prefix + path;
|
||||||
|
|
||||||
if (callback !== undefined && !utils.isFunction(callback)) {
|
if (callback !== undefined && !utils.isFunction(callback)) {
|
||||||
@@ -381,13 +405,17 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
uri: fullUri,
|
uri: fullUri,
|
||||||
method: method,
|
method: method,
|
||||||
withCredentials: false,
|
withCredentials: false,
|
||||||
json: false,
|
json: true, // we want a JSON response if we can
|
||||||
_matrix_opts: this.opts,
|
_matrix_opts: this.opts,
|
||||||
|
headers: {},
|
||||||
};
|
};
|
||||||
if (method == 'GET') {
|
if (method === 'GET') {
|
||||||
opts.qs = params;
|
opts.qs = params;
|
||||||
} else {
|
} else if (typeof params === "object") {
|
||||||
opts.form = params;
|
opts.json = params;
|
||||||
|
}
|
||||||
|
if (accessToken) {
|
||||||
|
opts.headers['Authorization'] = `Bearer ${accessToken}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defer = Promise.defer();
|
const defer = Promise.defer();
|
||||||
@@ -395,12 +423,7 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
opts,
|
opts,
|
||||||
requestCallback(defer, callback, this.opts.onlyData),
|
requestCallback(defer, callback, this.opts.onlyData),
|
||||||
);
|
);
|
||||||
// ID server does not always take JSON, so we can't use requests' 'json'
|
return defer.promise;
|
||||||
// option as we do with the home server, but it does return JSON, so
|
|
||||||
// parse it manually
|
|
||||||
return defer.promise.then(function(response) {
|
|
||||||
return JSON.parse(response);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -470,7 +493,7 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
const self = this;
|
const self = this;
|
||||||
requestPromise.catch(function(err) {
|
requestPromise.catch(function(err) {
|
||||||
if (err.errcode == 'M_UNKNOWN_TOKEN') {
|
if (err.errcode == 'M_UNKNOWN_TOKEN') {
|
||||||
self.event_emitter.emit("Session.logged_out");
|
self.event_emitter.emit("Session.logged_out", err);
|
||||||
} else if (err.errcode == 'M_CONSENT_NOT_GIVEN') {
|
} else if (err.errcode == 'M_CONSENT_NOT_GIVEN') {
|
||||||
self.event_emitter.emit(
|
self.event_emitter.emit(
|
||||||
"no_consent",
|
"no_consent",
|
||||||
@@ -525,76 +548,6 @@ module.exports.MatrixHttpApi.prototype = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform an authorised request to the homeserver with a specific path
|
|
||||||
* prefix which overrides the default for this call only. Useful for hitting
|
|
||||||
* different Matrix Client-Server versions.
|
|
||||||
* @param {Function} callback Optional. The callback to invoke on
|
|
||||||
* success/failure. See the promise return values for more information.
|
|
||||||
* @param {string} method The HTTP method e.g. "GET".
|
|
||||||
* @param {string} path The HTTP path <b>after</b> the supplied prefix e.g.
|
|
||||||
* "/createRoom".
|
|
||||||
* @param {Object} queryParams A dict of query params (these will NOT be
|
|
||||||
* urlencoded).
|
|
||||||
* @param {Object} data The HTTP JSON body.
|
|
||||||
* @param {string} prefix The full prefix to use e.g.
|
|
||||||
* "/_matrix/client/v2_alpha".
|
|
||||||
* @param {Number=} localTimeoutMs The maximum amount of time to wait before
|
|
||||||
* timing out the request. If not specified, there is no timeout.
|
|
||||||
* @return {module:client.Promise} Resolves to <code>{data: {Object},
|
|
||||||
* headers: {Object}, code: {Number}}</code>.
|
|
||||||
* If <code>onlyData</code> is set, this will resolve to the <code>data</code>
|
|
||||||
* object only.
|
|
||||||
* @return {module:http-api.MatrixError} Rejects with an error if a problem
|
|
||||||
* occurred. This includes network problems and Matrix-specific error JSON.
|
|
||||||
*
|
|
||||||
* @deprecated prefer authedRequest with opts.prefix
|
|
||||||
*/
|
|
||||||
authedRequestWithPrefix: function(callback, method, path, queryParams, data,
|
|
||||||
prefix, localTimeoutMs) {
|
|
||||||
return this.authedRequest(
|
|
||||||
callback, method, path, queryParams, data, {
|
|
||||||
localTimeoutMs: localTimeoutMs,
|
|
||||||
prefix: prefix,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform a request to the homeserver without any credentials but with a
|
|
||||||
* specific path prefix which overrides the default for this call only.
|
|
||||||
* Useful for hitting different Matrix Client-Server versions.
|
|
||||||
* @param {Function} callback Optional. The callback to invoke on
|
|
||||||
* success/failure. See the promise return values for more information.
|
|
||||||
* @param {string} method The HTTP method e.g. "GET".
|
|
||||||
* @param {string} path The HTTP path <b>after</b> the supplied prefix e.g.
|
|
||||||
* "/createRoom".
|
|
||||||
* @param {Object} queryParams A dict of query params (these will NOT be
|
|
||||||
* urlencoded).
|
|
||||||
* @param {Object} data The HTTP JSON body.
|
|
||||||
* @param {string} prefix The full prefix to use e.g.
|
|
||||||
* "/_matrix/client/v2_alpha".
|
|
||||||
* @param {Number=} localTimeoutMs The maximum amount of time to wait before
|
|
||||||
* timing out the request. If not specified, there is no timeout.
|
|
||||||
* @return {module:client.Promise} Resolves to <code>{data: {Object},
|
|
||||||
* headers: {Object}, code: {Number}}</code>.
|
|
||||||
* If <code>onlyData</code> is set, this will resolve to the <code>data</code>
|
|
||||||
* object only.
|
|
||||||
* @return {module:http-api.MatrixError} Rejects with an error if a problem
|
|
||||||
* occurred. This includes network problems and Matrix-specific error JSON.
|
|
||||||
*
|
|
||||||
* @deprecated prefer request with opts.prefix
|
|
||||||
*/
|
|
||||||
requestWithPrefix: function(callback, method, path, queryParams, data, prefix,
|
|
||||||
localTimeoutMs) {
|
|
||||||
return this.request(
|
|
||||||
callback, method, path, queryParams, data, {
|
|
||||||
localTimeoutMs: localTimeoutMs,
|
|
||||||
prefix: prefix,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a request to an arbitrary URL.
|
* Perform a request to an arbitrary URL.
|
||||||
* @param {Function} callback Optional. The callback to invoke on
|
* @param {Function} callback Optional. The callback to invoke on
|
||||||
@@ -883,7 +836,8 @@ function parseErrorResponse(response, body) {
|
|||||||
let err;
|
let err;
|
||||||
if (contentType) {
|
if (contentType) {
|
||||||
if (contentType.type === 'application/json') {
|
if (contentType.type === 'application/json') {
|
||||||
err = new module.exports.MatrixError(JSON.parse(body));
|
const jsonBody = typeof(body) === 'object' ? body : JSON.parse(body);
|
||||||
|
err = new module.exports.MatrixError(jsonBody);
|
||||||
} else if (contentType.type === 'text/plain') {
|
} else if (contentType.type === 'text/plain') {
|
||||||
err = new Error(`Server returned ${httpStatus} error: ${body}`);
|
err = new Error(`Server returned ${httpStatus} error: ${body}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import Promise from 'bluebird';
|
|||||||
const url = require("url");
|
const url = require("url");
|
||||||
|
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
import logger from '../src/logger';
|
import logger from './logger';
|
||||||
|
|
||||||
const EMAIL_STAGE_TYPE = "m.login.email.identity";
|
const EMAIL_STAGE_TYPE = "m.login.email.identity";
|
||||||
const MSISDN_STAGE_TYPE = "m.login.msisdn";
|
const MSISDN_STAGE_TYPE = "m.login.msisdn";
|
||||||
@@ -174,16 +174,19 @@ InteractiveAuth.prototype = {
|
|||||||
// The email can be validated out-of-band, but we need to provide the
|
// The email can be validated out-of-band, but we need to provide the
|
||||||
// creds so the HS can go & check it.
|
// creds so the HS can go & check it.
|
||||||
if (this._emailSid) {
|
if (this._emailSid) {
|
||||||
|
const creds = {
|
||||||
|
sid: this._emailSid,
|
||||||
|
client_secret: this._clientSecret,
|
||||||
|
};
|
||||||
|
if (await this._matrixClient.doesServerRequireIdServerParam()) {
|
||||||
const idServerParsedUrl = url.parse(
|
const idServerParsedUrl = url.parse(
|
||||||
this._matrixClient.getIdentityServerUrl(),
|
this._matrixClient.getIdentityServerUrl(),
|
||||||
);
|
);
|
||||||
|
creds.id_server = idServerParsedUrl.host;
|
||||||
|
}
|
||||||
authDict = {
|
authDict = {
|
||||||
type: EMAIL_STAGE_TYPE,
|
type: EMAIL_STAGE_TYPE,
|
||||||
threepid_creds: {
|
threepid_creds: creds,
|
||||||
sid: this._emailSid,
|
|
||||||
client_secret: this._clientSecret,
|
|
||||||
id_server: idServerParsedUrl.host,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -76,6 +77,7 @@ module.exports.InteractiveAuth = require("./interactive-auth");
|
|||||||
/** The {@link module:auto-discovery|AutoDiscovery} class. */
|
/** The {@link module:auto-discovery|AutoDiscovery} class. */
|
||||||
module.exports.AutoDiscovery = require("./autodiscovery").AutoDiscovery;
|
module.exports.AutoDiscovery = require("./autodiscovery").AutoDiscovery;
|
||||||
|
|
||||||
|
module.exports.SERVICE_TYPES = require('./service-types').SERVICE_TYPES;
|
||||||
|
|
||||||
module.exports.MemoryCryptoStore =
|
module.exports.MemoryCryptoStore =
|
||||||
require("./crypto/store/memory-crypto-store").default;
|
require("./crypto/store/memory-crypto-store").default;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const EventEmitter = require("events").EventEmitter;
|
|||||||
const utils = require("../utils");
|
const utils = require("../utils");
|
||||||
const EventTimeline = require("./event-timeline");
|
const EventTimeline = require("./event-timeline");
|
||||||
import {EventStatus} from "./event";
|
import {EventStatus} from "./event";
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
import Relations from './relations';
|
import Relations from './relations';
|
||||||
|
|
||||||
// var DEBUG = false;
|
// var DEBUG = false;
|
||||||
@@ -92,6 +92,13 @@ function EventTimelineSet(room, opts) {
|
|||||||
}
|
}
|
||||||
utils.inherits(EventTimelineSet, EventEmitter);
|
utils.inherits(EventTimelineSet, EventEmitter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the timelines in this set
|
||||||
|
* @return {module:models/event-timeline~EventTimeline[]} the timelines in this set
|
||||||
|
*/
|
||||||
|
EventTimelineSet.prototype.getTimelines = function() {
|
||||||
|
return this._timelines;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* Get the filter object this timeline set is filtered on, if any
|
* Get the filter object this timeline set is filtered on, if any
|
||||||
* @return {?Filter} the optional filter for this timelineSet
|
* @return {?Filter} the optional filter for this timelineSet
|
||||||
@@ -438,7 +445,6 @@ EventTimelineSet.prototype.addEventsToTimeline = function(events, toStartOfTimel
|
|||||||
if (backwardsIsLive || forwardsIsLive) {
|
if (backwardsIsLive || forwardsIsLive) {
|
||||||
// The live timeline should never be spliced into a non-live position.
|
// The live timeline should never be spliced into a non-live position.
|
||||||
// We use independent logging to better discover the problem at a glance.
|
// We use independent logging to better discover the problem at a glance.
|
||||||
logger.warn({backwardsIsLive, forwardsIsLive}); // debugging
|
|
||||||
if (backwardsIsLive) {
|
if (backwardsIsLive) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"Refusing to set a preceding existingTimeLine on our " +
|
"Refusing to set a preceding existingTimeLine on our " +
|
||||||
@@ -689,9 +695,12 @@ EventTimelineSet.prototype.compareEventOrdering = function(eventId1, eventId2) {
|
|||||||
* The type of relation involved, such as "m.annotation", "m.reference", "m.replace", etc.
|
* The type of relation involved, such as "m.annotation", "m.reference", "m.replace", etc.
|
||||||
* @param {String} eventType
|
* @param {String} eventType
|
||||||
* The relation event's type, such as "m.reaction", etc.
|
* The relation event's type, such as "m.reaction", etc.
|
||||||
|
* @throws If <code>eventId</code>, <code>relationType</code> or <code>eventType</code>
|
||||||
|
* are not valid.
|
||||||
*
|
*
|
||||||
* @returns {Relations}
|
* @returns {?Relations}
|
||||||
* A container for relation events.
|
* A container for relation events or undefined if there are no relation events for
|
||||||
|
* the relationType.
|
||||||
*/
|
*/
|
||||||
EventTimelineSet.prototype.getRelationsForEvent = function(
|
EventTimelineSet.prototype.getRelationsForEvent = function(
|
||||||
eventId, relationType, eventType,
|
eventId, relationType, eventType,
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ limitations under the License.
|
|||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import utils from '../utils.js';
|
import utils from '../utils.js';
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum for event statuses.
|
* Enum for event statuses.
|
||||||
* @readonly
|
* @readonly
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
module.exports.EventStatus = {
|
const EventStatus = {
|
||||||
/** The event was not sent and will no longer be retried. */
|
/** The event was not sent and will no longer be retried. */
|
||||||
NOT_SENT: "not_sent",
|
NOT_SENT: "not_sent",
|
||||||
|
|
||||||
@@ -49,6 +49,7 @@ module.exports.EventStatus = {
|
|||||||
/** The event was cancelled before it was successfully sent. */
|
/** The event was cancelled before it was successfully sent. */
|
||||||
CANCELLED: "cancelled",
|
CANCELLED: "cancelled",
|
||||||
};
|
};
|
||||||
|
module.exports.EventStatus = EventStatus;
|
||||||
|
|
||||||
const interns = {};
|
const interns = {};
|
||||||
function intern(str) {
|
function intern(str) {
|
||||||
@@ -124,7 +125,8 @@ module.exports.MatrixEvent = function MatrixEvent(
|
|||||||
this.forwardLooking = true;
|
this.forwardLooking = true;
|
||||||
this._pushActions = null;
|
this._pushActions = null;
|
||||||
this._replacingEvent = null;
|
this._replacingEvent = null;
|
||||||
this._locallyRedacted = false;
|
this._localRedactionEvent = null;
|
||||||
|
this._isCancelled = false;
|
||||||
|
|
||||||
this._clearEvent = {};
|
this._clearEvent = {};
|
||||||
|
|
||||||
@@ -229,7 +231,7 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
* @return {Object} The event content JSON, or an empty object.
|
* @return {Object} The event content JSON, or an empty object.
|
||||||
*/
|
*/
|
||||||
getOriginalContent: function() {
|
getOriginalContent: function() {
|
||||||
if (this._locallyRedacted) {
|
if (this._localRedactionEvent) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return this._clearEvent.content || this.event.content || {};
|
return this._clearEvent.content || this.event.content || {};
|
||||||
@@ -243,7 +245,7 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
* @return {Object} The event content JSON, or an empty object.
|
* @return {Object} The event content JSON, or an empty object.
|
||||||
*/
|
*/
|
||||||
getContent: function() {
|
getContent: function() {
|
||||||
if (this._locallyRedacted) {
|
if (this._localRedactionEvent) {
|
||||||
return {};
|
return {};
|
||||||
} else if (this._replacingEvent) {
|
} else if (this._replacingEvent) {
|
||||||
return this._replacingEvent.getContent()["m.new_content"] || {};
|
return this._replacingEvent.getContent()["m.new_content"] || {};
|
||||||
@@ -673,20 +675,20 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
unmarkLocallyRedacted: function() {
|
unmarkLocallyRedacted: function() {
|
||||||
const value = this._locallyRedacted;
|
const value = this._localRedactionEvent;
|
||||||
this._locallyRedacted = false;
|
this._localRedactionEvent = null;
|
||||||
if (this.event.unsigned) {
|
if (this.event.unsigned) {
|
||||||
this.event.unsigned.redacted_because = null;
|
this.event.unsigned.redacted_because = null;
|
||||||
}
|
}
|
||||||
return value;
|
return !!value;
|
||||||
},
|
},
|
||||||
|
|
||||||
markLocallyRedacted: function(redactionEvent) {
|
markLocallyRedacted: function(redactionEvent) {
|
||||||
if (this._locallyRedacted) {
|
if (this._localRedactionEvent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emit("Event.beforeRedaction", this, redactionEvent);
|
this.emit("Event.beforeRedaction", this, redactionEvent);
|
||||||
this._locallyRedacted = true;
|
this._localRedactionEvent = redactionEvent;
|
||||||
if (!this.event.unsigned) {
|
if (!this.event.unsigned) {
|
||||||
this.event.unsigned = {};
|
this.event.unsigned = {};
|
||||||
}
|
}
|
||||||
@@ -706,7 +708,7 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
throw new Error("invalid redaction_event in makeRedacted");
|
throw new Error("invalid redaction_event in makeRedacted");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._locallyRedacted = false;
|
this._localRedactionEvent = null;
|
||||||
|
|
||||||
this.emit("Event.beforeRedaction", this, redaction_event);
|
this.emit("Event.beforeRedaction", this, redaction_event);
|
||||||
|
|
||||||
@@ -867,7 +869,11 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
* @param {MatrixEvent?} newEvent the event with the replacing content, if any.
|
* @param {MatrixEvent?} newEvent the event with the replacing content, if any.
|
||||||
*/
|
*/
|
||||||
makeReplaced(newEvent) {
|
makeReplaced(newEvent) {
|
||||||
if (this.isRedacted()) {
|
// don't allow redacted events to be replaced.
|
||||||
|
// if newEvent is null we allow to go through though,
|
||||||
|
// as with local redaction, the replacing event might get
|
||||||
|
// cancelled, which should be reflected on the target event.
|
||||||
|
if (this.isRedacted() && newEvent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._replacingEvent !== newEvent) {
|
if (this._replacingEvent !== newEvent) {
|
||||||
@@ -877,15 +883,25 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the status of the event, or the replacing event in case `makeReplace` has been called.
|
* Returns the status of any associated edit or redaction
|
||||||
|
* (not for reactions/annotations as their local echo doesn't affect the orignal event),
|
||||||
|
* or else the status of the event.
|
||||||
*
|
*
|
||||||
* @return {EventStatus}
|
* @return {EventStatus}
|
||||||
*/
|
*/
|
||||||
replacementOrOwnStatus() {
|
getAssociatedStatus() {
|
||||||
if (this._replacingEvent) {
|
if (this._replacingEvent) {
|
||||||
return this._replacingEvent.status;
|
return this._replacingEvent.status;
|
||||||
} else {
|
} else if (this._localRedactionEvent) {
|
||||||
|
return this._localRedactionEvent.status;
|
||||||
|
}
|
||||||
return this.status;
|
return this.status;
|
||||||
|
},
|
||||||
|
|
||||||
|
getServerAggregatedRelation(relType) {
|
||||||
|
const relations = this.getUnsigned()["m.relations"];
|
||||||
|
if (relations) {
|
||||||
|
return relations[relType];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -895,11 +911,18 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
* @return {string?}
|
* @return {string?}
|
||||||
*/
|
*/
|
||||||
replacingEventId() {
|
replacingEventId() {
|
||||||
return this._replacingEvent && this._replacingEvent.getId();
|
const replaceRelation = this.getServerAggregatedRelation("m.replace");
|
||||||
|
if (replaceRelation) {
|
||||||
|
return replaceRelation.event_id;
|
||||||
|
} else if (this._replacingEvent) {
|
||||||
|
return this._replacingEvent.getId();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the event replacing the content of this event, if any.
|
* Returns the event replacing the content of this event, if any.
|
||||||
|
* Replacements are aggregated on the server, so this would only
|
||||||
|
* return an event in case it came down the sync, or for local echo of edits.
|
||||||
*
|
*
|
||||||
* @return {MatrixEvent?}
|
* @return {MatrixEvent?}
|
||||||
*/
|
*/
|
||||||
@@ -907,6 +930,31 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
return this._replacingEvent;
|
return this._replacingEvent;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the origin_server_ts of the event replacing the content of this event, if any.
|
||||||
|
*
|
||||||
|
* @return {Date?}
|
||||||
|
*/
|
||||||
|
replacingEventDate() {
|
||||||
|
const replaceRelation = this.getServerAggregatedRelation("m.replace");
|
||||||
|
if (replaceRelation) {
|
||||||
|
const ts = replaceRelation.origin_server_ts;
|
||||||
|
if (Number.isFinite(ts)) {
|
||||||
|
return new Date(ts);
|
||||||
|
}
|
||||||
|
} else if (this._replacingEvent) {
|
||||||
|
return this._replacingEvent.getDate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the event that wants to redact this event, but hasn't been sent yet.
|
||||||
|
* @return {MatrixEvent} the event
|
||||||
|
*/
|
||||||
|
localRedactionEvent() {
|
||||||
|
return this._localRedactionEvent;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For relations and redactions, returns the event_id this event is referring to.
|
* For relations and redactions, returns the event_id this event is referring to.
|
||||||
*
|
*
|
||||||
@@ -947,6 +995,25 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags an event as cancelled due to future conditions. For example, a verification
|
||||||
|
* request event in the same sync transaction may be flagged as cancelled to warn
|
||||||
|
* listeners that a cancellation event is coming down the same pipe shortly.
|
||||||
|
* @param {boolean} cancelled Whether the event is to be cancelled or not.
|
||||||
|
*/
|
||||||
|
flagCancelled(cancelled = true) {
|
||||||
|
this._isCancelled = cancelled;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether or not the event is flagged as cancelled. See flagCancelled() for
|
||||||
|
* more information.
|
||||||
|
* @returns {boolean} True if the event is cancelled, false otherwise.
|
||||||
|
*/
|
||||||
|
isCancelled() {
|
||||||
|
return this._isCancelled;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summarise the event as JSON for debugging. If encrypted, include both the
|
* Summarise the event as JSON for debugging. If encrypted, include both the
|
||||||
* decrypted and encrypted view of the event. This is named `toJSON` for use
|
* decrypted and encrypted view of the event. This is named `toJSON` for use
|
||||||
@@ -966,6 +1033,11 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
room_id: this.getRoomId(),
|
room_id: this.getRoomId(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// if this is a redaction then attach the redacts key
|
||||||
|
if (this.isRedaction()) {
|
||||||
|
event.redacts = this.event.redacts;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isEncrypted()) {
|
if (!this.isEncrypted()) {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
@@ -981,7 +1053,7 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
/* _REDACT_KEEP_KEY_MAP gives the keys we keep when an event is redacted
|
/* _REDACT_KEEP_KEY_MAP gives the keys we keep when an event is redacted
|
||||||
*
|
*
|
||||||
* This is specified here:
|
* This is specified here:
|
||||||
* http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#redactions
|
* http://matrix.org/speculator/spec/HEAD/client_server/latest.html#redactions
|
||||||
*
|
*
|
||||||
* Also:
|
* Also:
|
||||||
* - We keep 'unsigned' since that is created by the local server
|
* - We keep 'unsigned' since that is created by the local server
|
||||||
|
|||||||
@@ -301,10 +301,20 @@ export default class Relations extends EventEmitter {
|
|||||||
// event is known anyway.
|
// event is known anyway.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the all-knowning server tells us that the event at some point had
|
||||||
|
// this timestamp for its replacement, so any following replacement should definitely not be less
|
||||||
|
const replaceRelation =
|
||||||
|
this._targetEvent.getServerAggregatedRelation("m.replace");
|
||||||
|
const minTs = replaceRelation && replaceRelation.origin_server_ts;
|
||||||
|
|
||||||
return this.getRelations().reduce((last, event) => {
|
return this.getRelations().reduce((last, event) => {
|
||||||
if (event.getSender() !== this._targetEvent.getSender()) {
|
if (event.getSender() !== this._targetEvent.getSender()) {
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
if (minTs && minTs > event.getTs()) {
|
||||||
|
return last;
|
||||||
|
}
|
||||||
if (last && last.getTs() > event.getTs()) {
|
if (last && last.getTs() > event.getTs()) {
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ RoomMember.prototype.getDMInviter = function() {
|
|||||||
* "crop" or "scale".
|
* "crop" or "scale".
|
||||||
* @param {Boolean} allowDefault (optional) Passing false causes this method to
|
* @param {Boolean} allowDefault (optional) Passing false causes this method to
|
||||||
* return null if the user has no avatar image. Otherwise, a default image URL
|
* return null if the user has no avatar image. Otherwise, a default image URL
|
||||||
* will be returned. Default: true.
|
* will be returned. Default: true. (Deprecated)
|
||||||
* @param {Boolean} allowDirectLinks (optional) If true, the avatar URL will be
|
* @param {Boolean} allowDirectLinks (optional) If true, the avatar URL will be
|
||||||
* returned even if it is a direct hyperlink rather than a matrix content URL.
|
* returned even if it is a direct hyperlink rather than a matrix content URL.
|
||||||
* If false, any non-matrix content URLs will be ignored. Setting this option to
|
* If false, any non-matrix content URLs will be ignored. Setting this option to
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const EventEmitter = require("events").EventEmitter;
|
|||||||
|
|
||||||
const utils = require("../utils");
|
const utils = require("../utils");
|
||||||
const RoomMember = require("./room-member");
|
const RoomMember = require("./room-member");
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
// possible statuses for out-of-band member loading
|
// possible statuses for out-of-band member loading
|
||||||
const OOB_STATUS_NOTSTARTED = 1;
|
const OOB_STATUS_NOTSTARTED = 1;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2018, 2019 New Vector Ltd
|
Copyright 2018, 2019 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -29,7 +30,7 @@ const ContentRepo = require("../content-repo");
|
|||||||
const EventTimeline = require("./event-timeline");
|
const EventTimeline = require("./event-timeline");
|
||||||
const EventTimelineSet = require("./event-timeline-set");
|
const EventTimelineSet = require("./event-timeline-set");
|
||||||
|
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
import ReEmitter from '../ReEmitter';
|
import ReEmitter from '../ReEmitter';
|
||||||
|
|
||||||
// These constants are used as sane defaults when the homeserver doesn't support
|
// These constants are used as sane defaults when the homeserver doesn't support
|
||||||
@@ -38,7 +39,7 @@ import ReEmitter from '../ReEmitter';
|
|||||||
// room versions which are considered okay for people to run without being asked
|
// room versions which are considered okay for people to run without being asked
|
||||||
// to upgrade (ie: "stable"). Eventually, we should remove these when all homeservers
|
// to upgrade (ie: "stable"). Eventually, we should remove these when all homeservers
|
||||||
// return an m.room_versions capability.
|
// return an m.room_versions capability.
|
||||||
const KNOWN_SAFE_ROOM_VERSION = '1';
|
const KNOWN_SAFE_ROOM_VERSION = '4';
|
||||||
const SAFE_ROOM_VERSIONS = ['1', '2', '3', '4'];
|
const SAFE_ROOM_VERSIONS = ['1', '2', '3', '4'];
|
||||||
|
|
||||||
function synthesizeReceipt(userId, event, receiptType) {
|
function synthesizeReceipt(userId, event, receiptType) {
|
||||||
@@ -346,13 +347,30 @@ Room.prototype.userMayUpgradeRoom = function(userId) {
|
|||||||
Room.prototype.getPendingEvents = function() {
|
Room.prototype.getPendingEvents = function() {
|
||||||
if (this._opts.pendingEventOrdering !== "detached") {
|
if (this._opts.pendingEventOrdering !== "detached") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Cannot call getPendingEventList with pendingEventOrdering == " +
|
"Cannot call getPendingEvents with pendingEventOrdering == " +
|
||||||
this._opts.pendingEventOrdering);
|
this._opts.pendingEventOrdering);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._pendingEventList;
|
return this._pendingEventList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the pending event list contains a given event by ID.
|
||||||
|
*
|
||||||
|
* @param {string} eventId The event ID to check for.
|
||||||
|
* @return {boolean}
|
||||||
|
* @throws If <code>opts.pendingEventOrdering</code> was not 'detached'
|
||||||
|
*/
|
||||||
|
Room.prototype.hasPendingEvent = function(eventId) {
|
||||||
|
if (this._opts.pendingEventOrdering !== "detached") {
|
||||||
|
throw new Error(
|
||||||
|
"Cannot call hasPendingEvent with pendingEventOrdering == " +
|
||||||
|
this._opts.pendingEventOrdering);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._pendingEventList.some(event => event.getId() === eventId);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the live unfiltered timeline for this room.
|
* Get the live unfiltered timeline for this room.
|
||||||
*
|
*
|
||||||
@@ -763,7 +781,7 @@ Room.prototype.getBlacklistUnverifiedDevices = function() {
|
|||||||
* @param {string} resizeMethod The thumbnail resize method to use, either
|
* @param {string} resizeMethod The thumbnail resize method to use, either
|
||||||
* "crop" or "scale".
|
* "crop" or "scale".
|
||||||
* @param {boolean} allowDefault True to allow an identicon for this room if an
|
* @param {boolean} allowDefault True to allow an identicon for this room if an
|
||||||
* avatar URL wasn't explicitly set. Default: true.
|
* avatar URL wasn't explicitly set. Default: true. (Deprecated)
|
||||||
* @return {?string} the avatar URL or null.
|
* @return {?string} the avatar URL or null.
|
||||||
*/
|
*/
|
||||||
Room.prototype.getAvatarUrl = function(baseUrl, width, height, resizeMethod,
|
Room.prototype.getAvatarUrl = function(baseUrl, width, height, resizeMethod,
|
||||||
@@ -797,20 +815,20 @@ Room.prototype.getAvatarUrl = function(baseUrl, width, height, resizeMethod,
|
|||||||
* @return {array} The room's alias as an array of strings
|
* @return {array} The room's alias as an array of strings
|
||||||
*/
|
*/
|
||||||
Room.prototype.getAliases = function() {
|
Room.prototype.getAliases = function() {
|
||||||
const alias_strings = [];
|
const aliasStrings = [];
|
||||||
|
|
||||||
const alias_events = this.currentState.getStateEvents("m.room.aliases");
|
const aliasEvents = this.currentState.getStateEvents("m.room.aliases");
|
||||||
if (alias_events) {
|
if (aliasEvents) {
|
||||||
for (let i = 0; i < alias_events.length; ++i) {
|
for (let i = 0; i < aliasEvents.length; ++i) {
|
||||||
const alias_event = alias_events[i];
|
const aliasEvent = aliasEvents[i];
|
||||||
if (utils.isArray(alias_event.getContent().aliases)) {
|
if (utils.isArray(aliasEvent.getContent().aliases)) {
|
||||||
Array.prototype.push.apply(
|
Array.prototype.push.apply(
|
||||||
alias_strings, alias_event.getContent().aliases,
|
aliasStrings, aliasEvent.getContent().aliases,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return alias_strings;
|
return aliasStrings;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1039,6 +1057,18 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
|
|||||||
const redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
|
const redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
|
||||||
if (redactedEvent) {
|
if (redactedEvent) {
|
||||||
redactedEvent.makeRedacted(event);
|
redactedEvent.makeRedacted(event);
|
||||||
|
|
||||||
|
// If this is in the current state, replace it with the redacted version
|
||||||
|
if (redactedEvent.getStateKey()) {
|
||||||
|
const currentStateEvent = this.currentState.getStateEvents(
|
||||||
|
redactedEvent.getType(),
|
||||||
|
redactedEvent.getStateKey(),
|
||||||
|
);
|
||||||
|
if (currentStateEvent.getId() === redactedEvent.getId()) {
|
||||||
|
this.currentState.setStateEvents([redactedEvent]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.emit("Room.redaction", event, this);
|
this.emit("Room.redaction", event, this);
|
||||||
|
|
||||||
// TODO: we stash user displaynames (among other things) in
|
// TODO: we stash user displaynames (among other things) in
|
||||||
@@ -1157,7 +1187,7 @@ Room.prototype.addPendingEvent = function(event, txnId) {
|
|||||||
for (let i = 0; i < this._timelineSets.length; i++) {
|
for (let i = 0; i < this._timelineSets.length; i++) {
|
||||||
const timelineSet = this._timelineSets[i];
|
const timelineSet = this._timelineSets[i];
|
||||||
if (timelineSet.getFilter()) {
|
if (timelineSet.getFilter()) {
|
||||||
if (this._filter.filterRoomTimeline([event]).length) {
|
if (timelineSet.getFilter().filterRoomTimeline([event]).length) {
|
||||||
timelineSet.addEventToTimeline(event,
|
timelineSet.addEventToTimeline(event,
|
||||||
timelineSet.getLiveTimeline(), false);
|
timelineSet.getLiveTimeline(), false);
|
||||||
}
|
}
|
||||||
@@ -1186,7 +1216,7 @@ Room.prototype._aggregateNonLiveRelation = function(event) {
|
|||||||
for (let i = 0; i < this._timelineSets.length; i++) {
|
for (let i = 0; i < this._timelineSets.length; i++) {
|
||||||
const timelineSet = this._timelineSets[i];
|
const timelineSet = this._timelineSets[i];
|
||||||
if (timelineSet.getFilter()) {
|
if (timelineSet.getFilter()) {
|
||||||
if (this._filter.filterRoomTimeline([event]).length) {
|
if (timelineSet.getFilter().filterRoomTimeline([event]).length) {
|
||||||
timelineSet.aggregateRelations(event);
|
timelineSet.aggregateRelations(event);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1402,28 +1432,33 @@ Room.prototype.addLiveEvents = function(events, duplicateStrategy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < events.length; i++) {
|
for (i = 0; i < events.length; i++) {
|
||||||
if (events[i].getType() === "m.typing") {
|
|
||||||
this.currentState.setTypingEvent(events[i]);
|
|
||||||
} else if (events[i].getType() === "m.receipt") {
|
|
||||||
this.addReceipt(events[i]);
|
|
||||||
}
|
|
||||||
// N.B. account_data is added directly by /sync to avoid
|
|
||||||
// having to maintain an event.isAccountData() here
|
|
||||||
else {
|
|
||||||
// TODO: We should have a filter to say "only add state event
|
// TODO: We should have a filter to say "only add state event
|
||||||
// types X Y Z to the timeline".
|
// types X Y Z to the timeline".
|
||||||
this._addLiveEvent(events[i], duplicateStrategy);
|
this._addLiveEvent(events[i], duplicateStrategy);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds/handles ephemeral events such as typing notifications and read receipts.
|
||||||
|
* @param {MatrixEvent[]} events A list of events to process
|
||||||
|
*/
|
||||||
|
Room.prototype.addEphemeralEvents = function(events) {
|
||||||
|
for (const event of events) {
|
||||||
|
if (event.getType() === 'm.typing') {
|
||||||
|
this.currentState.setTypingEvent(event);
|
||||||
|
} else if (event.getType() === 'm.receipt') {
|
||||||
|
this.addReceipt(event);
|
||||||
|
} // else ignore - life is too short for us to care about these events
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes events from this room.
|
* Removes events from this room.
|
||||||
* @param {String[]} event_ids A list of event_ids to remove.
|
* @param {String[]} eventIds A list of eventIds to remove.
|
||||||
*/
|
*/
|
||||||
Room.prototype.removeEvents = function(event_ids) {
|
Room.prototype.removeEvents = function(eventIds) {
|
||||||
for (let i = 0; i < event_ids.length; ++i) {
|
for (let i = 0; i < eventIds.length; ++i) {
|
||||||
this.removeEvent(event_ids[i]);
|
this.removeEvent(eventIds[i]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ const DEFAULT_OVERRIDE_RULES = [
|
|||||||
key: "type",
|
key: "type",
|
||||||
pattern: "m.room.tombstone",
|
pattern: "m.room.tombstone",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
kind: "event_match",
|
||||||
|
key: "state_key",
|
||||||
|
pattern: "",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
"notify",
|
"notify",
|
||||||
@@ -52,6 +57,22 @@ const DEFAULT_OVERRIDE_RULES = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// For homeservers which don't support MSC2153 yet
|
||||||
|
rule_id: ".m.rule.reaction",
|
||||||
|
default: true,
|
||||||
|
enabled: true,
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
kind: "event_match",
|
||||||
|
key: "type",
|
||||||
|
pattern: "m.reaction",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
"dont_notify",
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -439,6 +460,38 @@ PushProcessor.actionListToActionsObject = function(actionlist) {
|
|||||||
return actionobj;
|
return actionobj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrites conditions on a client's push rules to match the defaults
|
||||||
|
* where applicable. Useful for upgrading push rules to more strict
|
||||||
|
* conditions when the server is falling behind on defaults.
|
||||||
|
* @param {object} incomingRules The client's existing push rules
|
||||||
|
* @returns {object} The rewritten rules
|
||||||
|
*/
|
||||||
|
PushProcessor.rewriteDefaultRules = function(incomingRules) {
|
||||||
|
let newRules = JSON.parse(JSON.stringify(incomingRules)); // deep clone
|
||||||
|
|
||||||
|
// These lines are mostly to make the tests happy. We shouldn't run into these
|
||||||
|
// properties missing in practice.
|
||||||
|
if (!newRules) newRules = {};
|
||||||
|
if (!newRules.global) newRules.global = {};
|
||||||
|
if (!newRules.global.override) newRules.global.override = [];
|
||||||
|
|
||||||
|
// Fix default override rules
|
||||||
|
newRules.global.override = newRules.global.override.map(r => {
|
||||||
|
const defaultRule = DEFAULT_OVERRIDE_RULES.find(d => d.rule_id === r.rule_id);
|
||||||
|
if (!defaultRule) return r;
|
||||||
|
|
||||||
|
// Copy over the actions, default, and conditions. Don't touch the user's
|
||||||
|
// preference.
|
||||||
|
r.default = defaultRule.default;
|
||||||
|
r.conditions = defaultRule.conditions;
|
||||||
|
r.actions = defaultRule.actions;
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
|
||||||
|
return newRules;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} PushAction
|
* @typedef {Object} PushAction
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
import logger from '../src/logger';
|
import logger from './logger';
|
||||||
|
|
||||||
// we schedule a callback at least this often, to check if we've missed out on
|
// we schedule a callback at least this often, to check if we've missed out on
|
||||||
// some wall-clock time due to being suspended.
|
// some wall-clock time due to being suspended.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import logger from '../src/logger';
|
import logger from './logger';
|
||||||
|
|
||||||
const DEBUG = false; // set true to enable console logging.
|
const DEBUG = false; // set true to enable console logging.
|
||||||
|
|
||||||
|
|||||||
20
src/service-types.js
Normal file
20
src/service-types.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const SERVICE_TYPES = Object.freeze({
|
||||||
|
IS: 'SERVICE_TYPE_IS', // An Identity Service
|
||||||
|
IM: 'SERVICE_TYPE_IM', // An Integration Manager
|
||||||
|
});
|
||||||
@@ -19,7 +19,7 @@ import Promise from 'bluebird';
|
|||||||
import SyncAccumulator from "../sync-accumulator";
|
import SyncAccumulator from "../sync-accumulator";
|
||||||
import utils from "../utils";
|
import utils from "../utils";
|
||||||
import * as IndexedDBHelpers from "../indexeddb-helpers";
|
import * as IndexedDBHelpers from "../indexeddb-helpers";
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
const VERSION = 3;
|
const VERSION = 3;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An IndexedDB store backend where the actual backend sits in a web
|
* An IndexedDB store backend where the actual backend sits in a web
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import LocalIndexedDBStoreBackend from "./indexeddb-local-backend.js";
|
import LocalIndexedDBStoreBackend from "./indexeddb-local-backend.js";
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class lives in the webworker and drives a LocalIndexedDBStoreBackend
|
* This class lives in the webworker and drives a LocalIndexedDBStoreBackend
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import LocalIndexedDBStoreBackend from "./indexeddb-local-backend.js";
|
|||||||
import RemoteIndexedDBStoreBackend from "./indexeddb-remote-backend.js";
|
import RemoteIndexedDBStoreBackend from "./indexeddb-remote-backend.js";
|
||||||
import User from "../models/user";
|
import User from "../models/user";
|
||||||
import {MatrixEvent} from "../models/event";
|
import {MatrixEvent} from "../models/event";
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an internal module. See {@link IndexedDBStore} for the public class.
|
* This is an internal module. See {@link IndexedDBStore} for the public class.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import utils from "./utils";
|
import utils from "./utils";
|
||||||
import logger from '../src/logger';
|
import logger from './logger';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
40
src/sync.js
40
src/sync.js
@@ -32,7 +32,8 @@ const Group = require('./models/group');
|
|||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
const Filter = require("./filter");
|
const Filter = require("./filter");
|
||||||
const EventTimeline = require("./models/event-timeline");
|
const EventTimeline = require("./models/event-timeline");
|
||||||
import logger from '../src/logger';
|
const PushProcessor = require("./pushprocessor");
|
||||||
|
import logger from './logger';
|
||||||
|
|
||||||
import {InvalidStoreError} from './errors';
|
import {InvalidStoreError} from './errors';
|
||||||
|
|
||||||
@@ -1030,8 +1031,9 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
// honour push rules that were previously cached. Base rules
|
// honour push rules that were previously cached. Base rules
|
||||||
// will be updated when we recieve push rules via getPushRules
|
// will be updated when we recieve push rules via getPushRules
|
||||||
// (see SyncApi.prototype.sync) before syncing over the network.
|
// (see SyncApi.prototype.sync) before syncing over the network.
|
||||||
if (accountDataEvent.getType() == 'm.push_rules') {
|
if (accountDataEvent.getType() === 'm.push_rules') {
|
||||||
client.pushRules = accountDataEvent.getContent();
|
const rules = accountDataEvent.getContent();
|
||||||
|
client.pushRules = PushProcessor.rewriteDefaultRules(rules);
|
||||||
}
|
}
|
||||||
client.emit("accountData", accountDataEvent);
|
client.emit("accountData", accountDataEvent);
|
||||||
return accountDataEvent;
|
return accountDataEvent;
|
||||||
@@ -1043,8 +1045,26 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
if (data.to_device && utils.isArray(data.to_device.events) &&
|
if (data.to_device && utils.isArray(data.to_device.events) &&
|
||||||
data.to_device.events.length > 0
|
data.to_device.events.length > 0
|
||||||
) {
|
) {
|
||||||
|
const cancelledKeyVerificationTxns = [];
|
||||||
data.to_device.events
|
data.to_device.events
|
||||||
.map(client.getEventMapper())
|
.map(client.getEventMapper())
|
||||||
|
.map((toDeviceEvent) => { // map is a cheap inline forEach
|
||||||
|
// We want to flag m.key.verification.start events as cancelled
|
||||||
|
// if there's an accompanying m.key.verification.cancel event, so
|
||||||
|
// we pull out the transaction IDs from the cancellation events
|
||||||
|
// so we can flag the verification events as cancelled in the loop
|
||||||
|
// below.
|
||||||
|
if (toDeviceEvent.getType() === "m.key.verification.cancel") {
|
||||||
|
const txnId = toDeviceEvent.getContent()['transaction_id'];
|
||||||
|
if (txnId) {
|
||||||
|
cancelledKeyVerificationTxns.push(txnId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// as mentioned above, .map is a cheap inline forEach, so return
|
||||||
|
// the unmodified event.
|
||||||
|
return toDeviceEvent;
|
||||||
|
})
|
||||||
.forEach(
|
.forEach(
|
||||||
function(toDeviceEvent) {
|
function(toDeviceEvent) {
|
||||||
const content = toDeviceEvent.getContent();
|
const content = toDeviceEvent.getContent();
|
||||||
@@ -1060,6 +1080,14 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toDeviceEvent.getType() === "m.key.verification.start"
|
||||||
|
|| toDeviceEvent.getType() === "m.key.verification.request") {
|
||||||
|
const txnId = content['transaction_id'];
|
||||||
|
if (cancelledKeyVerificationTxns.includes(txnId)) {
|
||||||
|
toDeviceEvent.flagCancelled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client.emit("toDeviceEvent", toDeviceEvent);
|
client.emit("toDeviceEvent", toDeviceEvent);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -1219,10 +1247,8 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
room.setSummary(joinObj.summary);
|
room.setSummary(joinObj.summary);
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: should we be adding ephemeralEvents to the timeline?
|
// we deliberately don't add ephemeral events to the timeline
|
||||||
// It feels like that for symmetry with room.addAccountData()
|
room.addEphemeralEvents(ephemeralEvents);
|
||||||
// there should be a room.addEphemeralEvents() or similar.
|
|
||||||
room.addLiveEvents(ephemeralEvents);
|
|
||||||
|
|
||||||
// we deliberately don't add accountData to the timeline
|
// we deliberately don't add accountData to the timeline
|
||||||
room.addAccountData(accountDataEvents);
|
room.addAccountData(accountDataEvents);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ limitations under the License.
|
|||||||
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
const EventTimeline = require("./models/event-timeline");
|
const EventTimeline = require("./models/event-timeline");
|
||||||
import logger from '../src/logger';
|
import logger from './logger';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -699,3 +700,11 @@ module.exports.globToRegexp = function(glob, extended) {
|
|||||||
}
|
}
|
||||||
return pat;
|
return pat;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.ensureNoTrailingSlash = function(url) {
|
||||||
|
if (url && url.endsWith("/")) {
|
||||||
|
return url.substr(0, url.length - 1);
|
||||||
|
} else {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
const utils = require("../utils");
|
const utils = require("../utils");
|
||||||
const EventEmitter = require("events").EventEmitter;
|
const EventEmitter = require("events").EventEmitter;
|
||||||
import logger from '../../src/logger';
|
import logger from '../logger';
|
||||||
const DEBUG = true; // set true to enable console logging.
|
const DEBUG = true; // set true to enable console logging.
|
||||||
|
|
||||||
// events: hangup, error(err), replaced(call), state(state, oldState)
|
// events: hangup, error(err), replaced(call), state(state, oldState)
|
||||||
@@ -61,9 +61,9 @@ function MatrixCall(opts) {
|
|||||||
this.URL = opts.URL;
|
this.URL = opts.URL;
|
||||||
// Array of Objects with urls, username, credential keys
|
// Array of Objects with urls, username, credential keys
|
||||||
this.turnServers = opts.turnServers || [];
|
this.turnServers = opts.turnServers || [];
|
||||||
if (this.turnServers.length === 0) {
|
if (this.turnServers.length === 0 && this.client.isFallbackICEServerAllowed()) {
|
||||||
this.turnServers.push({
|
this.turnServers.push({
|
||||||
urls: [MatrixCall.FALLBACK_STUN_SERVER],
|
urls: [MatrixCall.FALLBACK_ICE_SERVER],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
utils.forEach(this.turnServers, function(server) {
|
utils.forEach(this.turnServers, function(server) {
|
||||||
@@ -92,8 +92,8 @@ function MatrixCall(opts) {
|
|||||||
}
|
}
|
||||||
/** The length of time a call can be ringing for. */
|
/** The length of time a call can be ringing for. */
|
||||||
MatrixCall.CALL_TIMEOUT_MS = 60000;
|
MatrixCall.CALL_TIMEOUT_MS = 60000;
|
||||||
/** The fallback server to use for STUN. */
|
/** The fallback ICE server to use for STUN or TURN protocols. */
|
||||||
MatrixCall.FALLBACK_STUN_SERVER = 'stun:stun.l.google.com:19302';
|
MatrixCall.FALLBACK_ICE_SERVER = 'stun:turn.matrix.org';
|
||||||
/** An error code when the local client failed to create an offer. */
|
/** An error code when the local client failed to create an offer. */
|
||||||
MatrixCall.ERR_LOCAL_OFFER_FAILED = "local_offer_failed";
|
MatrixCall.ERR_LOCAL_OFFER_FAILED = "local_offer_failed";
|
||||||
/**
|
/**
|
||||||
@@ -665,7 +665,7 @@ MatrixCall.prototype._maybeGotUserMediaForAnswer = function(stream) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
self.peerConn.createAnswer(function(description) {
|
self.peerConn.createAnswer(function(description) {
|
||||||
debuglog("Created answer: " + description);
|
debuglog("Created answer: ", description);
|
||||||
self.peerConn.setLocalDescription(description, function() {
|
self.peerConn.setLocalDescription(description, function() {
|
||||||
self._answerContent = {
|
self._answerContent = {
|
||||||
version: 0,
|
version: 0,
|
||||||
@@ -754,7 +754,7 @@ MatrixCall.prototype._receivedAnswer = function(msg) {
|
|||||||
*/
|
*/
|
||||||
MatrixCall.prototype._gotLocalOffer = function(description) {
|
MatrixCall.prototype._gotLocalOffer = function(description) {
|
||||||
const self = this;
|
const self = this;
|
||||||
debuglog("Created offer: " + description);
|
debuglog("Created offer: ", description);
|
||||||
|
|
||||||
if (self.state == 'ended') {
|
if (self.state == 'ended') {
|
||||||
debuglog("Ignoring newly created offer on call ID " + self.callId +
|
debuglog("Ignoring newly created offer on call ID " + self.callId +
|
||||||
@@ -1217,24 +1217,9 @@ const _placeCallWithConstraints = function(self, constraints) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const _createPeerConnection = function(self) {
|
const _createPeerConnection = function(self) {
|
||||||
let servers = self.turnServers;
|
|
||||||
if (self.webRtc.vendor === "mozilla") {
|
|
||||||
// modify turnServers struct to match what mozilla expects.
|
|
||||||
servers = [];
|
|
||||||
for (let i = 0; i < self.turnServers.length; i++) {
|
|
||||||
for (let j = 0; j < self.turnServers[i].urls.length; j++) {
|
|
||||||
servers.push({
|
|
||||||
url: self.turnServers[i].urls[j],
|
|
||||||
username: self.turnServers[i].username,
|
|
||||||
credential: self.turnServers[i].credential,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pc = new self.webRtc.RtcPeerConnection({
|
const pc = new self.webRtc.RtcPeerConnection({
|
||||||
iceTransportPolicy: self.forceTURN ? 'relay' : undefined,
|
iceTransportPolicy: self.forceTURN ? 'relay' : undefined,
|
||||||
iceServers: servers,
|
iceServers: self.turnServers,
|
||||||
});
|
});
|
||||||
pc.oniceconnectionstatechange = hookCallback(self, self._onIceConnectionStateChanged);
|
pc.oniceconnectionstatechange = hookCallback(self, self._onIceConnectionStateChanged);
|
||||||
pc.onsignalingstatechange = hookCallback(self, self._onSignallingStateChanged);
|
pc.onsignalingstatechange = hookCallback(self, self._onSignallingStateChanged);
|
||||||
@@ -1352,7 +1337,9 @@ module.exports.setVideoInput = function(deviceId) { videoInput = deviceId; };
|
|||||||
* @param {MatrixClient} client The client instance to use.
|
* @param {MatrixClient} client The client instance to use.
|
||||||
* @param {string} roomId The room the call is in.
|
* @param {string} roomId The room the call is in.
|
||||||
* @param {Object?} options DEPRECATED optional options map.
|
* @param {Object?} options DEPRECATED optional options map.
|
||||||
* @param {boolean} options.forceTURN DEPRECATED whether relay through TURN should be forced. This option is deprecated - use opts.forceTURN when creating the matrix client since it's only possible to set this option on outbound calls.
|
* @param {boolean} options.forceTURN DEPRECATED whether relay through TURN should be
|
||||||
|
* forced. This option is deprecated - use opts.forceTURN when creating the matrix client
|
||||||
|
* since it's only possible to set this option on outbound calls.
|
||||||
* @return {MatrixCall} the call or null if the browser doesn't support calling.
|
* @return {MatrixCall} the call or null if the browser doesn't support calling.
|
||||||
*/
|
*/
|
||||||
module.exports.createNewMatrixCall = function(client, roomId, options) {
|
module.exports.createNewMatrixCall = function(client, roomId, options) {
|
||||||
@@ -1383,6 +1370,12 @@ module.exports.createNewMatrixCall = function(client, roomId, options) {
|
|||||||
return getUserMedia.apply(w.navigator, arguments);
|
return getUserMedia.apply(w.navigator, arguments);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Firefox throws on so little as accessing the RTCPeerConnection when operating in
|
||||||
|
// a secure mode. There's some information at https://bugzilla.mozilla.org/show_bug.cgi?id=1542616
|
||||||
|
// though the concern is that the browser throwing a SecurityError will brick the
|
||||||
|
// client creation process.
|
||||||
|
try {
|
||||||
webRtc.RtcPeerConnection = (
|
webRtc.RtcPeerConnection = (
|
||||||
w.RTCPeerConnection || w.webkitRTCPeerConnection || w.mozRTCPeerConnection
|
w.RTCPeerConnection || w.webkitRTCPeerConnection || w.mozRTCPeerConnection
|
||||||
);
|
);
|
||||||
@@ -1401,6 +1394,12 @@ module.exports.createNewMatrixCall = function(client, roomId, options) {
|
|||||||
} else if (w.RTCPeerConnection) {
|
} else if (w.RTCPeerConnection) {
|
||||||
webRtc.vendor = "generic";
|
webRtc.vendor = "generic";
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Failed to set up WebRTC object: possible browser interference?");
|
||||||
|
logger.error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!webRtc.RtcIceCandidate || !webRtc.RtcSessionDescription ||
|
if (!webRtc.RtcIceCandidate || !webRtc.RtcSessionDescription ||
|
||||||
!webRtc.RtcPeerConnection || !webRtc.getUserMedia) {
|
!webRtc.RtcPeerConnection || !webRtc.getUserMedia) {
|
||||||
return null; // WebRTC is not supported.
|
return null; // WebRTC is not supported.
|
||||||
|
|||||||
Reference in New Issue
Block a user