Compare commits

..

85 Commits

Author SHA1 Message Date
David Baker 9c6973a46a v0.7.4 2017-01-16 13:03:35 +00:00
David Baker b439be8fb5 Prepare changelog for v0.7.4 2017-01-16 13:03:34 +00:00
David Baker edaf84a78f Fix non-screensharing calls 2017-01-16 12:28:52 +00:00
David Baker bb7c9b2227 v0.7.4-rc.1 2017-01-13 10:57:51 +00:00
David Baker 8538d7881a Prepare changelog for v0.7.4-rc.1 2017-01-13 10:57:50 +00:00
David Baker df4ae597a5 Include eslint config files in distribution
Otherwise linting will fail if you try to run it
2017-01-12 16:54:49 +00:00
David Baker 8c512bce9e Use less ancient version of node
ESLint errors with old node (at least without --harmony) so use
a more sensible version
2017-01-12 16:20:49 +00:00
David Baker 4775010452 Be quiet npm
we don't need the commands you're running in the xml file
2017-01-12 16:07:44 +00:00
David Baker 03f4b15c61 Merge pull request #321 from matrix-org/dbkr/dont_polyfill
Remove babel-polyfill
2017-01-12 15:56:57 +00:00
David Baker d8c23c0dcb Merge pull request #320 from matrix-org/dbkr/build_process_es6
Update build process for ES6
2017-01-12 15:50:35 +00:00
David Baker 4ab261b89f Add eslint:recommends
Turn off / tweak some options from it. Fix a double-definition.
Add an eslint config to the spec directory to tell it about the
jasmine magic globals.
2017-01-12 15:05:42 +00:00
David Baker e057956ede Add google eslint rules as a base
Remove some we don't care about. Set some other ones we do care
about but don't currently adhere to to warn. Set the max warnings
threshold to the current number of warnings, so we don't introduce
more of them. Fix a bunch of legit lint errors and add exceptions
to various places in the test code that does funny things with
'this'.
2017-01-12 14:35:58 +00:00
David Baker 543b9cf0ce Run lint on prepublish, not build
and make everything errors, so now you can do local builds with
lint failures, but CI will fail and you can't release.
2017-01-12 12:57:24 +00:00
David Baker 591b56d794 it's build now, not compile 2017-01-12 12:55:50 +00:00
David Baker 7f8375d864 Lint spec as well as src 2017-01-12 12:51:59 +00:00
David Baker 31af4bbeb5 Fix jsdoc errors in spec/ 2017-01-12 12:51:22 +00:00
David Baker 0a11404be2 Fix legitimate JSDoc errors 2017-01-12 11:46:07 +00:00
David Baker ff723980ac Add exceptions to eslintrc for JSDoc
To allow things we've been OK about previously
2017-01-12 11:26:17 +00:00
David Baker 0dfd60ad5e Merge compile target into build 2017-01-11 19:02:25 +00:00
David Baker 18f57a2100 Remove babel-polyfill
react-sdk's tests are failing because babel-polyfill is being
pulled in twice.

As per https://babeljs.io/docs/usage/polyfill/ babel-polyfill is
"intended to be used in an application rather than a library/tool".
From a library, we're limited to things that don't modify globals,
since the js env is not ours to start screwing around with.
2017-01-11 18:52:49 +00:00
David Baker 9b5cb3a631 Update build process for ES6
* Make npm run build run npm compile (it needs the output)
 * Switch to ESlint so we can actually use ES6 without the linter
   crying.
2017-01-11 18:11:47 +00:00
David Baker 09e4e4709f Merge pull request #319 from matrix-org/dbkr/babel_is_not_a_package
'babel' is not a babel package anymore
2017-01-11 17:35:57 +00:00
David Baker 00895f00e6 Empty commit to force rebuild 2017-01-11 17:30:15 +00:00
David Baker c57be7b966 'babel' is not a babel package anymore
and so is just redundant as you have babel-cli which is what you
actually want.
2017-01-11 17:17:19 +00:00
Kegsay 51d94e63f4 Merge pull request #318 from matrix-org/kegan/es6
Add Babel for ES6 support
2017-01-11 11:47:19 +00:00
Kegan Dougal 6daeec838f Add start script 2017-01-11 11:25:24 +00:00
Kegan Dougal 53f23939c1 Review comments 2017-01-11 10:59:33 +00:00
Kegan Dougal 0bbec9e182 Appease linters 2017-01-11 10:40:20 +00:00
Kegan Dougal 101970dcd9 Merge branch 'develop' into kegan/es6 2017-01-11 10:35:01 +00:00
Kegan Dougal 6644151d19 Add babel-polyfill to include more ES6 goodies 2017-01-11 10:32:52 +00:00
Kegan Dougal 548ffdced1 es2015 not env 2017-01-11 10:27:51 +00:00
Kegan Dougal cba4b24b23 Add /lib to ignore as with react SDK 2017-01-11 10:24:31 +00:00
Kegan Dougal 3e922c2d41 Add babel dep and config 2017-01-11 10:19:46 +00:00
Kegan Dougal ae6a409cc2 Move /lib to /src 2017-01-11 10:09:04 +00:00
David Baker 94d79edbd0 Merge pull request #317 from matrix-org/dbkr/move_screen_sharing_error
Move screen sharing check/error
2017-01-11 10:08:12 +00:00
David Baker 4dc331d629 Move screen sharing check/error
Because the https check only applies in the browser
2017-01-10 18:36:54 +00:00
David Baker 18131735d7 Cheekily fix screen sharing with audio 2017-01-10 14:24:09 +00:00
Richard van der Hoff 2a51e7a665 Merge pull request #316 from matrix-org/kegan/release-script-dies-if-uncommitted-changes
release.sh: Bail early if there are uncommitted changes
2017-01-04 14:05:23 +00:00
Kegan Dougal df7ac77113 Review comments 2017-01-04 13:57:51 +00:00
Kegan Dougal 1b222249c4 Reset ret before reusing it 2017-01-04 11:56:01 +00:00
Kegan Dougal 126967cb90 release.sh: Bail early if there are uncommitted changes 2017-01-04 11:47:33 +00:00
Kegan Dougal 2afa381cae Merge branch 'master' into develop 2017-01-04 11:34:24 +00:00
Kegan Dougal 05cbc217a0 Add more docs to explain how to do releases 2017-01-04 11:27:34 +00:00
Kegan Dougal 54eec40d20 v0.7.3 2017-01-04 11:25:43 +00:00
Kegan Dougal 3ab34f911b Prepare changelog for v0.7.3 2017-01-04 11:25:42 +00:00
Kegan Dougal d6e4d0a417 Remove RELEASING.md
vdh says it is out of date and is misleading and should be removed, so removing it.
2017-01-04 11:16:59 +00:00
Kegan Dougal fac40f5183 Styling and ES5 only 2017-01-04 11:10:24 +00:00
Kegsay ce684a6628 Merge pull request #310 from Deadid/develop
User presence list feature
2017-01-04 10:38:09 +00:00
Richard van der Hoff 14fac241f7 bump to olm 2.1.0 2016-12-23 14:36:22 +00:00
Sergiy Makhov 335579e250 Changed paramater type to string array. Removed bad doc 2016-12-23 08:51:41 +02:00
Kegsay c8565be3a5 Merge pull request #313 from matrix-org/kegan/timeouts-for-everyone
Allow clients the ability to set a default local timeout
2016-12-22 17:17:25 +00:00
Kegan Dougal 76e76269cf Review comments 2016-12-22 17:09:41 +00:00
Kegan Dougal 3c43e2718d More JSDoc 2016-12-22 17:07:52 +00:00
Kegan Dougal f2676772c8 Allow clients the ability to set a default local timeout
Will be used to fix matrix-org/matrix-appservice-irc#328
2016-12-22 16:54:42 +00:00
David Baker c9bf4270fc Merge pull request #312 from matrix-org/dbkr/delete_threepid
Add API to delete threepid
2016-12-22 15:06:54 +00:00
David Baker 41ddb7660b Add doc details 2016-12-22 15:01:20 +00:00
Luke Barnard b3e93ffadf Add getDate function to MatrixEvent (#311)
* Add `getDate` function to MatrixEvent

`getDate` can be used to get the timestamp of the event as a `Date` instance. 

Adds handleRemoteEcho function to be called on change of internal event of MatrixEvent, so that the internal `_date` can be updated when a remote echo replaces a local one.
2016-12-22 14:07:26 +00:00
David Baker 582576b1c3 Add API to delete threepid
Uses https://github.com/matrix-org/synapse/pull/1714
2016-12-21 18:48:42 +00:00
David Baker 456135a6ec get latest changes after updating changelog 2016-12-16 17:39:24 +00:00
Luke Barnard c7357952ec v0.7.2 2016-12-15 17:37:22 +00:00
Luke Barnard b796246d9d Prepare changelog for v0.7.2 2016-12-15 17:37:22 +00:00
Sergiy Makhov 598e48e39b User presence list feature 2016-12-15 13:22:25 +02:00
Richard van der Hoff 2e3c349c5e Merge pull request #309 from matrix-org/rav/fix_olm_import
Bump to Olm 2.0
2016-12-14 18:59:13 +00:00
Richard van der Hoff 7f4ff352e8 Merge pull request #307 from matrix-org/rav/check_payload_length
Sanity check payload length before encrypting
2016-12-14 14:34:02 +00:00
Richard van der Hoff b11bff5a5b Bump to Olm 2.0
... since the code requires it. Also update the tests.
2016-12-14 14:15:31 +00:00
Richard van der Hoff 223bd459f6 Merge pull request #308 from matrix-org/rav/remove_dead_ohai
Remove dead _sendPingToDevice function
2016-12-14 13:56:12 +00:00
Richard van der Hoff ed1673c66c Remove dead _sendPingToDevice function
This is no longer used; remove it.
2016-12-14 12:38:33 +00:00
Richard van der Hoff 0fda43b603 Sanity check payload length before encrypting
... to give better error messages.
2016-12-14 11:54:35 +00:00
David Baker d3b63c592e Set GIT_COMITTER_EMAIL
so the release tag verifies correctly
2016-12-12 15:37:09 +00:00
Luke Barnard a32af7d77c Merge pull request #306 from matrix-org/luke/feature-AS-room-publication
Add setRoomDirectoryVisibilityAppService
2016-12-12 14:20:43 +00:00
Luke Barnard 742d942baa Add setRoomDirectoryVisibilityAppService
This is for setting the publicity of a room that is bridged to a 3rd party network. This change reflects the second bullet point of https://github.com/matrix-org/synapse/pull/1676#issue-193810881.
2016-12-12 13:52:44 +00:00
David Baker 73e86bfc5d Don't depend on having a build_dir
to get latest changes
2016-12-09 20:17:25 +00:00
David Baker 0a4c41c958 Merge branch 'master' into develop 2016-12-09 19:30:15 +00:00
David Baker 95f56f95ec Merge pull request #305 from matrix-org/dbkr/signed_releases
Update release script to do signed releases
2016-12-09 19:22:01 +00:00
David Baker 67e75fb7af Invoke changelog_head from outside the pushd
so we know the relative path is right
2016-12-09 19:19:40 +00:00
David Baker ebda89d1ae Add changelog_head.py 2016-12-09 17:34:11 +00:00
David Baker 5ce5299651 Update release script to do signed releases
if a signing ID is set in release_config.yaml

Also set the release text to the relevant changelog entry
2016-12-09 17:04:19 +00:00
David Baker 41bd518182 Merge pull request #304 from matrix-org/rav/await_pending_downloads
e2e: Wait for pending device lists
2016-12-08 18:41:53 +00:00
Richard van der Hoff 739e94302d fix test fail 2016-12-08 18:06:22 +00:00
Richard van der Hoff e54541aecf e2e: Wait for pending device lists
When we send a megolm message, wait for any existing key download to complete.
2016-12-08 16:37:29 +00:00
Richard van der Hoff 338c707579 Merge pull request #303 from matrix-org/rav/new_session_after_blacklist
Start a new megolm session when devices are blacklisted
2016-12-08 16:08:27 +00:00
Richard van der Hoff 301ab01911 Start a new megolm session when devices are blacklisted
If we have shared the session with a device which is subsequently blacklisted,
we need to start a new session for the next message.

Rather than doing this proactively (which would be subject to false-positives
and require slightly awkward tracking of who we had shared the session with),
we check the list of who we have shared the session with on each send, and
start a new session if any of them are blocked.

Fixes https://github.com/vector-im/riot-web/issues/2146.
2016-12-08 13:39:58 +00:00
Richard van der Hoff 99089c0f5f Merge pull request #302 from matrix-org/rav/send_to_our_devices
E2E: Download our own devicelist on startup
2016-12-07 11:43:24 +00:00
Richard van der Hoff ec124847d7 Test for self-keyshare
Make sure that we share keys with our own devices.
2016-12-07 11:14:36 +00:00
Richard van der Hoff 89ced19874 E2E: Download our own devicelist on startup
Make sure we get a list of our own devices when starting a new client.

Fixes https://github.com/vector-im/riot-web/issues/2676.
2016-12-06 17:09:21 +00:00
73 changed files with 1145 additions and 543 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"presets": ["es2015"]
}
+59
View File
@@ -0,0 +1,59 @@
module.exports = {
parserOptions: {
ecmaVersion: 6,
sourceType: "module",
ecmaFeatures: {
}
},
env: {
browser: true,
node: true,
},
extends: ["eslint:recommended", "google"],
rules: {
// rules we've always adhered to
"max-len": ["error", {
code: 90,
ignoreComments: true,
}],
curly: ["error", "multi-line"],
// loosen jsdoc requirements a little
"require-jsdoc": ["error", {
require: {
FunctionDeclaration: false,
}
}],
"valid-jsdoc": ["error", {
requireParamDescription: false,
requireReturn: false,
requireReturnDescription: false,
}],
// rules we do not want from eslint-recommended
"no-console": ["off"],
"no-constant-condition": ["off"],
"no-empty": ["error", { "allowEmptyCatch": true }],
// rules we do not want from the google styleguide
"object-curly-spacing": ["off"],
"spaced-comment": ["off"],
// in principle we prefer single quotes, but life is too short
quotes: ["off"],
// rules we'd ideally like to adhere to, but the current
// code does not (in most cases because it's still ES5)
// we set these to warnings, and assert that the number
// of warnings doesn't exceed a given threshold
"no-var": ["warn"],
"comma-dangle": ["warn"],
"brace-style": ["warn"],
"prefer-rest-params": ["warn"],
"prefer-spread": ["warn"],
"one-var": ["warn"],
"padded-blocks": ["warn"],
"no-extend-native": ["warn"],
"camelcase": ["warn"],
}
}
+1
View File
@@ -7,6 +7,7 @@ lib-cov
out
reports
/dist
/lib
# version file and tarball created by 'npm pack'
/git-revision.txt
-11
View File
@@ -1,11 +0,0 @@
{
"node": true,
"jasmine": true,
"nonew": true,
"curly": true,
"forin": true,
"freeze": false,
"undef": true,
"unused": "vars"
}
+55
View File
@@ -1,3 +1,58 @@
Changes in [0.7.4](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.4) (2017-01-16)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.4-rc.1...v0.7.4)
* Fix non-conference calling
Changes in [0.7.4-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.4-rc.1) (2017-01-13)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.3...v0.7.4-rc.1)
* Remove babel-polyfill
[\#321](https://github.com/matrix-org/matrix-js-sdk/pull/321)
* Update build process for ES6
[\#320](https://github.com/matrix-org/matrix-js-sdk/pull/320)
* 'babel' is not a babel package anymore
[\#319](https://github.com/matrix-org/matrix-js-sdk/pull/319)
* Add Babel for ES6 support
[\#318](https://github.com/matrix-org/matrix-js-sdk/pull/318)
* Move screen sharing check/error
[\#317](https://github.com/matrix-org/matrix-js-sdk/pull/317)
* release.sh: Bail early if there are uncommitted changes
[\#316](https://github.com/matrix-org/matrix-js-sdk/pull/316)
Changes in [0.7.3](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.3) (2017-01-04)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.2...v0.7.3)
* User presence list feature
[\#310](https://github.com/matrix-org/matrix-js-sdk/pull/310)
* Allow clients the ability to set a default local timeout
[\#313](https://github.com/matrix-org/matrix-js-sdk/pull/313)
* Add API to delete threepid
[\#312](https://github.com/matrix-org/matrix-js-sdk/pull/312)
Changes in [0.7.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.2) (2016-12-15)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.1...v0.7.2)
* Bump to Olm 2.0
[\#309](https://github.com/matrix-org/matrix-js-sdk/pull/309)
* Sanity check payload length before encrypting
[\#307](https://github.com/matrix-org/matrix-js-sdk/pull/307)
* Remove dead _sendPingToDevice function
[\#308](https://github.com/matrix-org/matrix-js-sdk/pull/308)
* Add setRoomDirectoryVisibilityAppService
[\#306](https://github.com/matrix-org/matrix-js-sdk/pull/306)
* Update release script to do signed releases
[\#305](https://github.com/matrix-org/matrix-js-sdk/pull/305)
* e2e: Wait for pending device lists
[\#304](https://github.com/matrix-org/matrix-js-sdk/pull/304)
* Start a new megolm session when devices are blacklisted
[\#303](https://github.com/matrix-org/matrix-js-sdk/pull/303)
* E2E: Download our own devicelist on startup
[\#302](https://github.com/matrix-org/matrix-js-sdk/pull/302)
Changes in [0.7.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.1) (2016-12-09)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.1-rc.1...v0.7.1)
-14
View File
@@ -1,14 +0,0 @@
There is a script `release.sh` which does the following, but if you need to do
a release manually, here are the steps:
- `git checkout -b release-v0.x.x`
- Update `CHANGELOG.md`
- `npm version 0.x.x`
- Merge `release-v0.x.x` onto `master`.
- Push `master`.
- Push the tag: `git push --tags`
- `npm publish`
- Generate documentation: `npm run gendoc` (this outputs HTML to `.jsdoc`)
- Copy the documentation from `.jsdoc` to the `gh-pages` branch and update `index.html`
- Merge `master` onto `develop`.
- Push `develop`.
+3 -8
View File
@@ -2,7 +2,7 @@
export NVM_DIR="/home/jenkins/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm use 0.10
nvm use 4
npm install
RC=0
@@ -14,13 +14,8 @@ function fail {
npm test || fail "npm test finished with return code $?"
jshint --reporter=checkstyle -c .jshint lib spec > jshint.xml ||
fail "jshint finished with return code $?"
gjslint --unix_mode --disable 0131,0211,0200,0222,0212 \
--max_line_length 90 \
-r lib/ -r spec/ > gjslint.log ||
fail "gjslint finished with return code $?"
npm run -s lint -- -f checkstyle > eslint.xml ||
fail "eslint finished with return code $?"
# delete the old tarball, if it exists
rm -f matrix-js-sdk-*.tgz
+14 -7
View File
@@ -1,17 +1,18 @@
{
"name": "matrix-js-sdk",
"version": "0.7.1",
"version": "0.7.4",
"description": "Matrix Client-Server SDK for Javascript",
"main": "index.js",
"scripts": {
"test": "istanbul cover --report cobertura --config .istanbul.yml -i \"lib/**/*.js\" jasmine-node -- spec --verbose --junitreport --captureExceptions",
"check": "jasmine-node spec --verbose --junitreport --captureExceptions",
"gendoc": "jsdoc -r lib -P package.json -R README.md -d .jsdoc",
"build": "jshint -c .jshint lib/ && rimraf dist && mkdir dist && browserify --exclude olm browser-index.js -o dist/browser-matrix.js --ignore-missing && uglifyjs -c -m -o dist/browser-matrix.min.js dist/browser-matrix.js",
"start": "babel -s -w -d lib src",
"build": "babel -s -d lib src && rimraf dist && mkdir dist && browserify --exclude olm browser-index.js -o dist/browser-matrix.js --ignore-missing && uglifyjs -c -m -o dist/browser-matrix.min.js dist/browser-matrix.js",
"dist": "npm run build",
"watch": "watchify --exclude olm browser-index.js -o dist/browser-matrix-dev.js -v",
"lint": "jshint -c .jshint lib spec && gjslint --unix_mode --disable 0131,0211,0200,0222,0212 --max_line_length 90 -r spec/ -r lib/",
"prepublish": "git rev-parse HEAD > git-revision.txt"
"lint": "eslint --max-warnings 3480 src spec",
"prepublish": "npm run lint && npm run build && git rev-parse HEAD > git-revision.txt"
},
"repository": {
"url": "https://github.com/matrix-org/matrix-js-sdk"
@@ -23,6 +24,8 @@
"author": "matrix.org",
"license": "Apache-2.0",
"files": [
".eslintrc.js",
"spec/.eslintrc.js",
"CHANGELOG.md",
"CONTRIBUTING.rst",
"LICENSE",
@@ -37,7 +40,8 @@
"lib",
"package.json",
"release.sh",
"spec"
"spec",
"src"
],
"dependencies": {
"another-json": "^0.2.0",
@@ -47,15 +51,18 @@
"request": "^2.53.0"
},
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-preset-es2015": "^6.18.0",
"eslint": "^3.13.1",
"eslint-config-google": "^0.7.1",
"istanbul": "^0.3.13",
"jasmine-node": "^1.14.5",
"jsdoc": "^3.4.0",
"jshint": "^2.8.0",
"rimraf": "^2.5.4",
"uglifyjs": "^2.4.10",
"watchify": "^3.2.1"
},
"optionalDependencies": {
"olm": "https://matrix.org/packages/npm/olm/olm-1.3.0.tgz"
"olm": "https://matrix.org/packages/npm/olm/olm-2.1.0.tgz"
}
}
+54 -9
View File
@@ -1,12 +1,12 @@
#!/bin/bash
#
# Script to perform a release of matrix-js-sdk. Performs the steps documented
# in RELEASING.md
# Script to perform a release of matrix-js-sdk.
#
# Requires:
# github-changelog-generator; to install, do
# github-changelog-generator; install via:
# pip install git+https://github.com/matrix-org/github-changelog-generator.git
# jq; install from your distibution's package manager (https://stedolan.github.io/jq/)
# jq; install from your distribution's package manager (https://stedolan.github.io/jq/)
# hub; install via brew (OSX) or source/pre-compiled binaries (debian) (https://github.com/github/hub) - Tested on v2.2.9
set -e
@@ -32,6 +32,16 @@ if [ "$ret" -eq 0 ]; then
exit
fi
if ! git diff-index --quiet --cached HEAD; then
echo "this git checkout has staged (uncommitted) changes. Refusing to release."
exit
fi
if ! git diff-files --quiet; then
echo "this git checkout has uncommitted changes. Refusing to release."
exit
fi
skip_changelog=
skip_jsdoc=
changelog_file="CHANGELOG.md"
@@ -111,19 +121,29 @@ if [ -z "$skip_changelog" ]; then
git commit "$changelog_file" -m "Prepare changelog for $tag"
fi
fi
latest_changes=`mktemp`
cat "${changelog_file}" | `dirname $0`/scripts/changelog_head.py > "${latest_changes}"
set -x
# Bump package.json, build the dist, and tag
# Bump package.json and build the dist
echo "npm version"
# npm version will automatically commit its modification
# and make a release tag. We don't want it to create the tag
# because github will do that, but we can only turn off both
# of these behaviours, so we have to manually commit the
# result.
# because it can only sign with the default key, but we can
# only turn off both of these behaviours, so we have to
# manually commit the result.
npm version --no-git-tag-version "$release"
git commit package.json -m "$tag"
# figure out if we should be signing this release
signing_id=
if [ -f release_config.yaml ]; then
signing_id=`cat release_config.yaml | python -c "import yaml; import sys; print yaml.load(sys.stdin)['signing_id']"`
fi
# If there is a 'dist' script in the package.json,
# run it in a separate checkout of the project, then
# upload any files in the 'dist' directory as release
@@ -145,9 +165,16 @@ if [ $dodist -eq 0 ]; then
# We haven't tagged yet, so tell the dist script what version
# it's building
DIST_VERSION="$tag" npm run dist
popd
for i in "$builddir"/dist/*; do
assets="$assets -a $i"
if [ -n "$signing_id" ]
then
gpg -u "$signing_id" --armor --output "$i".asc --detach-sig "$i"
assets="$assets -a $i.asc"
fi
done
fi
@@ -155,15 +182,33 @@ fi
# a branch it doesn't have)
git push origin "$rel_branch"
if [ -n "$signing_id" ]; then
# make a signed tag
# gnupg seems to fail to get the right tty device unless we set it here
GIT_COMMITTER_EMAIL="$signing_id" GPG_TTY=`tty` git tag -u "$signing_id" -F "${latest_changes}" "$tag"
else
git tag -a -F "${latest_changes}" "$tag"
fi
# push the tag
git push origin "$tag"
hubflags=''
if [ $prerelease -eq 1 ]; then
hubflags='-p'
fi
hub release create $hubflags $assets -m "$tag" "$tag"
release_text=`mktemp`
echo "$tag" > "${release_text}"
echo >> "${release_text}"
cat "${latest_changes}" >> "${release_text}"
hub release create $hubflags $assets -f "${release_text}" "$tag"
if [ $dodist -eq 0 ]; then
rm -rf "$builddir"
fi
rm "${release_text}"
rm "${latest_changes}"
if [ -z "$skip_jsdoc" ]; then
echo "generating jsdocs"
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env python
"""
Outputs the body of the first entry of changelog file on stdin
"""
import re
import sys
found_first_header = False
for line in sys.stdin:
line = line.strip()
if re.match(r"^Changes in \[.*\]", line):
if found_first_header:
break
found_first_header = True
elif not re.match(r"^=+$", line) and len(line) > 0:
print line
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
env: {
jasmine: true,
},
}
+19 -1
View File
@@ -383,6 +383,15 @@ function recvMessage(httpBackend, client, sender, message) {
function aliStartClient() {
expectAliKeyUpload().catch(test_utils.failTest);
// ali will try to query her own keys on start
aliHttpBackend.when("POST", "/keys/query").respond(200, function(path, content) {
expect(content.device_keys[aliUserId]).toEqual({});
var result = {};
result[aliUserId] = {};
return {device_keys: result};
});
startClient(aliHttpBackend, aliClient);
return aliHttpBackend.flush().then(function() {
console.log("Ali client started");
@@ -391,6 +400,15 @@ function aliStartClient() {
function bobStartClient() {
expectBobKeyUpload().catch(test_utils.failTest);
// bob will try to query his own keys on start
bobHttpBackend.when("POST", "/keys/query").respond(200, function(path, content) {
expect(content.device_keys[bobUserId]).toEqual({});
var result = {};
result[bobUserId] = {};
return {device_keys: result};
});
startClient(bobHttpBackend, bobClient);
return bobHttpBackend.flush().then(function() {
console.log("Bob client started");
@@ -445,7 +463,7 @@ describe("MatrixClient crypto", function() {
}
beforeEach(function() {
test_utils.beforeEach(this);
test_utils.beforeEach(this); // eslint-disable-line no-invalid-this
aliStorage = new sdk.WebStorageSessionStore(new test_utils.MockStorageApi());
aliHttpBackend = new HttpBackend();
@@ -10,7 +10,7 @@ describe("MatrixClient events", function() {
var selfAccessToken = "aseukfgwef";
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
httpBackend = new HttpBackend();
sdk.request(httpBackend.requestFn);
client = sdk.createClient({
@@ -100,7 +100,7 @@ describe("getEventTimeline support", function() {
var client;
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
httpBackend = new HttpBackend();
sdk.request(httpBackend.requestFn);
});
@@ -221,7 +221,7 @@ describe("MatrixClient event timelines", function() {
var client, httpBackend;
beforeEach(function(done) {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
httpBackend = new HttpBackend();
sdk.request(httpBackend.requestFn);
@@ -415,7 +415,7 @@ describe("MatrixClient event timelines", function() {
};
});
var tl0, tl2, tl3;
var tl0, tl3;
client.getEventTimeline(timelineSet, EVENTS[0].event_id
).then(function(tl) {
expect(tl.getEvents().length).toEqual(1);
@@ -423,7 +423,6 @@ describe("MatrixClient event timelines", function() {
return client.getEventTimeline(timelineSet, EVENTS[2].event_id);
}).then(function(tl) {
expect(tl.getEvents().length).toEqual(1);
tl2 = tl;
return client.getEventTimeline(timelineSet, EVENTS[3].event_id);
}).then(function(tl) {
expect(tl.getEvents().length).toEqual(1);
+2 -2
View File
@@ -15,7 +15,7 @@ describe("MatrixClient", function() {
var accessToken = "aseukfgwef";
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
httpBackend = new HttpBackend();
store = new MatrixInMemoryStore();
@@ -363,7 +363,7 @@ describe("MatrixClient", function() {
assertObjectContains(res.chaz.dev2, {
verified: 0, // DeviceVerification.UNVERIFIED
keys: { "ed25519:dev2" : ed25519key },
keys: { "ed25519:dev2": ed25519key },
algorithms: ["2"],
unsigned: { "ghi": "def" },
});
+1 -1
View File
@@ -53,7 +53,7 @@ describe("MatrixClient opts", function() {
};
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
httpBackend = new HttpBackend();
});
+1 -1
View File
@@ -14,7 +14,7 @@ describe("MatrixClient retrying", function() {
var room;
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
httpBackend = new HttpBackend();
sdk.request(httpBackend.requestFn);
scheduler = new sdk.MatrixScheduler();
@@ -101,7 +101,7 @@ describe("MatrixClient room timelines", function() {
}
beforeEach(function(done) {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
httpBackend = new HttpBackend();
sdk.request(httpBackend.requestFn);
client = sdk.createClient({
+1 -1
View File
@@ -18,7 +18,7 @@ describe("MatrixClient syncing", function() {
var roomTwo = "!bar:localhost";
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
httpBackend = new HttpBackend();
sdk.request(httpBackend.requestFn);
client = sdk.createClient({
+505 -223
View File
@@ -29,6 +29,8 @@ var utils = require('../../lib/utils');
var test_utils = require('../test-utils');
var MockHttpBackend = require('../mock-request');
var ROOM_ID = "!room:id";
/**
* Wrapper for a MockStorageApi, MockHttpBackend and MatrixClient
*
@@ -59,12 +61,25 @@ function TestClient(userId, deviceId, accessToken) {
/**
* start the client, and wait for it to initialise.
*
* @param {object?} existingDevices the list of our existing devices to return from
* the /query request. Defaults to empty device list
* @return {Promise}
*/
TestClient.prototype.start = function() {
TestClient.prototype.start = function(existingDevices) {
var self = this;
this.httpBackend.when("GET", "/pushrules").respond(200, {});
this.httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
this.httpBackend.when('POST', '/keys/query').respond(200, function(path, content) {
expect(content.device_keys[self.userId]).toEqual({});
var res = existingDevices;
if (!res) {
res = { device_keys: {} };
res.device_keys[self.userId] = {};
}
return res;
});
this.httpBackend.when("POST", "/keys/upload").respond(200, function(path, content) {
expect(content.one_time_keys).not.toBeDefined();
expect(content.device_keys).toBeDefined();
@@ -243,20 +258,129 @@ function encryptGroupSessionKey(opts) {
});
}
/**
* get a /sync response which contains a single room (ROOM_ID),
* with the members given
*
* @param {string[]} roomMembers
*
* @return {object} event
*/
function getSyncResponse(roomMembers) {
var roomResponse = {
state: {
events: [
test_utils.mkEvent({
type: 'm.room.encryption',
skey: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
}),
],
}
};
for (var i = 0; i < roomMembers.length; i++) {
roomResponse.state.events.push(
test_utils.mkMembership({
mship: 'join',
sender: roomMembers[i],
})
);
}
var syncResponse = {
next_batch: 1,
rooms: {
join: {},
},
};
syncResponse.rooms.join[ROOM_ID] = roomResponse;
return syncResponse;
}
describe("megolm", function() {
if (!sdk.CRYPTO_ENABLED) {
return;
}
var ROOM_ID = "!room:id";
var testOlmAccount;
var testSenderKey;
var aliceTestClient;
var testDeviceKeys;
beforeEach(function(done) {
test_utils.beforeEach(this);
/**
* Get the device keys for testOlmAccount in a format suitable for a
* response to /keys/query
*
* @param {string} userId The user ID to query for
* @returns {Object} The fake query response
*/
function getTestKeysQueryResponse(userId) {
var testE2eKeys = JSON.parse(testOlmAccount.identity_keys());
var testDeviceKeys = {
algorithms: ['m.olm.v1.curve25519-aes-sha2', 'm.megolm.v1.aes-sha2'],
device_id: 'DEVICE_ID',
keys: {
'curve25519:DEVICE_ID': testE2eKeys.curve25519,
'ed25519:DEVICE_ID': testE2eKeys.ed25519,
},
user_id: userId,
};
var j = anotherjson.stringify(testDeviceKeys);
var sig = testOlmAccount.sign(j);
testDeviceKeys.signatures = {};
testDeviceKeys.signatures[userId] = {
'ed25519:DEVICE_ID': sig,
};
var queryResponse = {
device_keys: {},
};
queryResponse.device_keys[userId] = {
'DEVICE_ID': testDeviceKeys,
};
return queryResponse;
}
/**
* Get a one-time key for testOlmAccount in a format suitable for a
* response to /keys/claim
* @param {string} userId The user ID to query for
* @returns {Object} The fake key claim response
*/
function getTestKeysClaimResponse(userId) {
testOlmAccount.generate_one_time_keys(1);
var testOneTimeKeys = JSON.parse(testOlmAccount.one_time_keys());
testOlmAccount.mark_keys_as_published();
var keyId = utils.keys(testOneTimeKeys.curve25519)[0];
var oneTimeKey = testOneTimeKeys.curve25519[keyId];
var keyResult = {
'key': oneTimeKey,
};
var j = anotherjson.stringify(keyResult);
var sig = testOlmAccount.sign(j);
keyResult.signatures = {};
keyResult.signatures[userId] = {
'ed25519:DEVICE_ID': sig,
};
var claimResponse = {one_time_keys: {}};
claimResponse.one_time_keys[userId] = {
'DEVICE_ID': {},
};
claimResponse.one_time_keys[userId].DEVICE_ID['signed_curve25519:' + keyId] =
keyResult;
return claimResponse;
}
beforeEach(function() {
test_utils.beforeEach(this); // eslint-disable-line no-invalid-this
aliceTestClient = new TestClient(
"@alice:localhost", "xzcvb", "akjgkrgjs"
@@ -266,25 +390,6 @@ describe("megolm", function() {
testOlmAccount.create();
var testE2eKeys = JSON.parse(testOlmAccount.identity_keys());
testSenderKey = testE2eKeys.curve25519;
testDeviceKeys = {
algorithms: ['m.olm.v1.curve25519-aes-sha2', 'm.megolm.v1.aes-sha2'],
device_id: 'DEVICE_ID',
keys: {
'curve25519:DEVICE_ID': testE2eKeys.curve25519,
'ed25519:DEVICE_ID': testE2eKeys.ed25519,
},
user_id: '@bob:xyz',
};
var j = anotherjson.stringify(testDeviceKeys);
var sig = testOlmAccount.sign(j);
testDeviceKeys.signatures = {
'@bob:xyz': {
'ed25519:DEVICE_ID': sig,
},
};
return aliceTestClient.start().nodeify(done);
});
afterEach(function() {
@@ -292,44 +397,47 @@ describe("megolm", function() {
});
it("Alice receives a megolm message", function(done) {
var p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
return aliceTestClient.start().then(function() {
var p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
var groupSession = new Olm.OutboundGroupSession();
groupSession.create();
var groupSession = new Olm.OutboundGroupSession();
groupSession.create();
// make the room_key event
var roomKeyEncrypted = encryptGroupSessionKey({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
groupSession: groupSession,
room_id: ROOM_ID,
});
// make the room_key event
var roomKeyEncrypted = encryptGroupSessionKey({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
groupSession: groupSession,
room_id: ROOM_ID,
});
// encrypt a message with the group session
var messageEncrypted = encryptMegolmEvent({
senderKey: testSenderKey,
groupSession: groupSession,
room_id: ROOM_ID,
});
// encrypt a message with the group session
var messageEncrypted = encryptMegolmEvent({
senderKey: testSenderKey,
groupSession: groupSession,
room_id: ROOM_ID,
});
// Alice gets both the events in a single sync
var syncResponse = {
next_batch: 1,
to_device: {
events: [roomKeyEncrypted],
},
rooms: {
join: {},
},
};
syncResponse.rooms.join[ROOM_ID] = {
timeline: {
events: [messageEncrypted],
},
};
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
return aliceTestClient.httpBackend.flush("/sync", 1).then(function() {
// Alice gets both the events in a single sync
var syncResponse = {
next_batch: 1,
to_device: {
events: [roomKeyEncrypted],
},
rooms: {
join: {},
},
};
syncResponse.rooms.join[ROOM_ID] = {
timeline: {
events: [messageEncrypted],
},
};
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
return aliceTestClient.httpBackend.flush("/sync", 1);
}).then(function() {
var room = aliceTestClient.client.getRoom(ROOM_ID);
var event = room.getLiveTimeline().getEvents()[0];
expect(event.getContent().body).toEqual('42');
@@ -337,65 +445,67 @@ describe("megolm", function() {
});
it("Alice gets a second room_key message", function(done) {
var p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
return aliceTestClient.start().then(function() {
var p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
var groupSession = new Olm.OutboundGroupSession();
groupSession.create();
var groupSession = new Olm.OutboundGroupSession();
groupSession.create();
// make the room_key event
var roomKeyEncrypted1 = encryptGroupSessionKey({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
groupSession: groupSession,
room_id: ROOM_ID,
});
// make the room_key event
var roomKeyEncrypted1 = encryptGroupSessionKey({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
groupSession: groupSession,
room_id: ROOM_ID,
});
// encrypt a message with the group session
var messageEncrypted = encryptMegolmEvent({
senderKey: testSenderKey,
groupSession: groupSession,
room_id: ROOM_ID,
});
// encrypt a message with the group session
var messageEncrypted = encryptMegolmEvent({
senderKey: testSenderKey,
groupSession: groupSession,
room_id: ROOM_ID,
});
// make a second room_key event now that we have advanced the group
// session.
var roomKeyEncrypted2 = encryptGroupSessionKey({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
groupSession: groupSession,
room_id: ROOM_ID,
});
// make a second room_key event now that we have advanced the group
// session.
var roomKeyEncrypted2 = encryptGroupSessionKey({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
groupSession: groupSession,
room_id: ROOM_ID,
});
// on the first sync, send the best room key
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, {
next_batch: 1,
to_device: {
events: [roomKeyEncrypted1],
},
});
// on the first sync, send the best room key
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, {
next_batch: 1,
to_device: {
events: [roomKeyEncrypted1],
},
});
// on the second sync, send the advanced room key, along with the
// message. This simulates the situation where Alice has been sent a
// later copy of the room key and is reloading the client.
var syncResponse2 = {
next_batch: 2,
to_device: {
events: [roomKeyEncrypted2],
},
rooms: {
join: {},
},
};
syncResponse2.rooms.join[ROOM_ID] = {
timeline: {
events: [messageEncrypted],
},
};
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse2);
// on the second sync, send the advanced room key, along with the
// message. This simulates the situation where Alice has been sent a
// later copy of the room key and is reloading the client.
var syncResponse2 = {
next_batch: 2,
to_device: {
events: [roomKeyEncrypted2],
},
rooms: {
join: {},
},
};
syncResponse2.rooms.join[ROOM_ID] = {
timeline: {
events: [messageEncrypted],
},
};
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse2);
return aliceTestClient.httpBackend.flush("/sync", 2).then(function() {
return aliceTestClient.httpBackend.flush("/sync", 2);
}).then(function() {
var room = aliceTestClient.client.getRoom(ROOM_ID);
var event = room.getLiveTimeline().getEvents()[0];
expect(event.getContent().body).toEqual('42');
@@ -403,54 +513,30 @@ describe("megolm", function() {
});
it('Alice sends a megolm message', function(done) {
// establish an olm session with alice
var p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
var p2pSession;
var olmEvent = encryptOlmEvent({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
});
return aliceTestClient.start().then(function() {
var syncResponse = getSyncResponse(['@bob:xyz']);
var syncResponse = {
next_batch: 1,
to_device: {
events: [olmEvent],
},
rooms: {
join: {},
},
};
syncResponse.rooms.join[ROOM_ID] = {
state: {
events: [
test_utils.mkEvent({
type: 'm.room.encryption',
skey: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
}),
test_utils.mkMembership({
mship: 'join',
sender: '@bob:xyz',
}),
],
},
};
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
// establish an olm session with alice
p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
var inboundGroupSession;
return aliceTestClient.httpBackend.flush('/sync', 1).then(function() {
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(200, {
device_keys: {
'@bob:xyz': {
'DEVICE_ID': testDeviceKeys,
},
}
var olmEvent = encryptOlmEvent({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
});
syncResponse.to_device = { events: [olmEvent] };
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
return aliceTestClient.httpBackend.flush('/sync', 1);
}).then(function() {
var inboundGroupSession;
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(
200, getTestKeysQueryResponse('@bob:xyz')
);
aliceTestClient.httpBackend.when(
'PUT', '/sendToDevice/m.room.encrypted/'
).respond(200, function(path, content) {
@@ -468,9 +554,11 @@ describe("megolm", function() {
'PUT', '/send/'
).respond(200, function(path, content) {
var ct = content.ciphertext;
var decrypted = JSON.parse(inboundGroupSession.decrypt(ct));
var r = inboundGroupSession.decrypt(ct);
console.log('Decrypted received megolm message', r);
console.log('Decrypted received megolm message', decrypted);
expect(r.message_index).toEqual(0);
var decrypted = JSON.parse(r.plaintext);
expect(decrypted.type).toEqual('m.room.message');
expect(decrypted.content.body).toEqual('test');
@@ -487,32 +575,12 @@ describe("megolm", function() {
});
it("Alice shouldn't do a second /query for non-e2e-capable devices", function(done) {
var syncResponse = {
next_batch: 1,
rooms: {
join: {},
},
};
syncResponse.rooms.join[ROOM_ID] = {
state: {
events: [
test_utils.mkEvent({
type: 'm.room.encryption',
skey: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
}),
test_utils.mkMembership({
mship: 'join',
sender: '@bob:xyz',
}),
],
},
};
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
return aliceTestClient.start().then(function() {
var syncResponse = getSyncResponse(['@bob:xyz']);
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
return aliceTestClient.httpBackend.flush('/sync', 1).then(function() {
return aliceTestClient.httpBackend.flush('/sync', 1);
}).then(function() {
console.log("Forcing alice to download our device keys");
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(200, {
@@ -543,54 +611,28 @@ describe("megolm", function() {
it("We shouldn't attempt to send to blocked devices", function(done) {
// establish an olm session with alice
var p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
return aliceTestClient.start().then(function() {
var syncResponse = getSyncResponse(['@bob:xyz']);
var olmEvent = encryptOlmEvent({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
});
// establish an olm session with alice
var p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
var syncResponse = {
next_batch: 1,
to_device: {
events: [olmEvent],
},
rooms: {
join: {},
},
};
var olmEvent = encryptOlmEvent({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
});
syncResponse.rooms.join[ROOM_ID] = {
state: {
events: [
test_utils.mkEvent({
type: 'm.room.encryption',
skey: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
}),
test_utils.mkMembership({
mship: 'join',
sender: '@bob:xyz',
}),
],
},
};
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
syncResponse.to_device = { events: [olmEvent] };
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
return aliceTestClient.httpBackend.flush('/sync', 1).then(function() {
return aliceTestClient.httpBackend.flush('/sync', 1);
}).then(function() {
console.log('Forcing alice to download our device keys');
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(200, {
device_keys: {
'@bob:xyz': {
'DEVICE_ID': testDeviceKeys,
},
}
});
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(
200, getTestKeysQueryResponse('@bob:xyz')
);
return q.all([
aliceTestClient.client.downloadKeys(['@bob:xyz']),
@@ -614,4 +656,244 @@ describe("megolm", function() {
}).nodeify(done);
});
it("We should start a new megolm session when a device is blocked", function(done) {
var p2pSession;
var megolmSessionId;
return aliceTestClient.start().then(function() {
var syncResponse = getSyncResponse(['@bob:xyz']);
// establish an olm session with alice
p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
var olmEvent = encryptOlmEvent({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
});
syncResponse.to_device = { events: [olmEvent] };
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
return aliceTestClient.httpBackend.flush('/sync', 1);
}).then(function() {
console.log('Telling alice to send a megolm message');
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(
200, getTestKeysQueryResponse('@bob:xyz')
);
aliceTestClient.httpBackend.when(
'PUT', '/sendToDevice/m.room.encrypted/'
).respond(200, function(path, content) {
console.log('sendToDevice: ', content);
var m = content.messages['@bob:xyz'].DEVICE_ID;
var ct = m.ciphertext[testSenderKey];
expect(ct.type).toEqual(1); // normal message
var decrypted = JSON.parse(p2pSession.decrypt(ct.type, ct.body));
console.log('decrypted sendToDevice:', decrypted);
expect(decrypted.type).toEqual('m.room_key');
megolmSessionId = decrypted.content.session_id;
return {};
});
aliceTestClient.httpBackend.when(
'PUT', '/send/'
).respond(200, function(path, content) {
console.log('/send:', content);
expect(content.session_id).toEqual(megolmSessionId);
return {
event_id: '$event_id',
};
});
return q.all([
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test'),
aliceTestClient.httpBackend.flush(),
]);
}).then(function() {
console.log('Telling alice to block our device');
aliceTestClient.client.setDeviceBlocked('@bob:xyz', 'DEVICE_ID');
console.log('Telling alice to send another megolm message');
aliceTestClient.httpBackend.when(
'PUT', '/send/'
).respond(200, function(path, content) {
console.log('/send:', content);
expect(content.session_id).not.toEqual(megolmSessionId);
return {
event_id: '$event_id',
};
});
return q.all([
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test2'),
aliceTestClient.httpBackend.flush(),
]);
}).nodeify(done);
});
// https://github.com/vector-im/riot-web/issues/2676
it("Alice should send to her other devices", function(done) {
// for this test, we make the testOlmAccount be another of Alice's devices.
// it ought to get include in messages Alice sends.
var p2pSession;
var inboundGroupSession;
var decrypted;
return aliceTestClient.start(
getTestKeysQueryResponse(aliceTestClient.userId)
).then(function() {
// an encrypted room with just alice
var syncResponse = {
next_batch: 1,
rooms: {
join: {},
},
};
syncResponse.rooms.join[ROOM_ID] = {
state: {
events: [
test_utils.mkEvent({
type: 'm.room.encryption',
skey: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
}),
test_utils.mkMembership({
mship: 'join',
sender: aliceTestClient.userId,
}),
],
},
};
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
return aliceTestClient.httpBackend.flush();
}).then(function() {
aliceTestClient.httpBackend.when('POST', '/keys/claim').respond(
200, function(path, content)
{
expect(content.one_time_keys[aliceTestClient.userId].DEVICE_ID)
.toEqual("signed_curve25519");
return getTestKeysClaimResponse(aliceTestClient.userId);
});
aliceTestClient.httpBackend.when(
'PUT', '/sendToDevice/m.room.encrypted/'
).respond(200, function(path, content) {
console.log("sendToDevice: ", content);
var m = content.messages[aliceTestClient.userId].DEVICE_ID;
var ct = m.ciphertext[testSenderKey];
expect(ct.type).toEqual(0); // pre-key message
p2pSession = new Olm.Session();
p2pSession.create_inbound(testOlmAccount, ct.body);
var decrypted = JSON.parse(p2pSession.decrypt(ct.type, ct.body));
expect(decrypted.type).toEqual('m.room_key');
inboundGroupSession = new Olm.InboundGroupSession();
inboundGroupSession.create(decrypted.content.session_key);
return {};
});
aliceTestClient.httpBackend.when(
'PUT', '/send/'
).respond(200, function(path, content) {
var ct = content.ciphertext;
var r = inboundGroupSession.decrypt(ct);
console.log('Decrypted received megolm message', r);
decrypted = JSON.parse(r.plaintext);
return {
event_id: '$event_id',
};
});
return q.all([
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test'),
aliceTestClient.httpBackend.flush(),
]);
}).then(function() {
expect(decrypted.type).toEqual('m.room.message');
expect(decrypted.content.body).toEqual('test');
}).nodeify(done);
});
it('Alice should wait for device list to complete when sending a megolm message',
function(done) {
var p2pSession;
var inboundGroupSession;
var downloadPromise;
var sendPromise;
aliceTestClient.httpBackend.when(
'PUT', '/sendToDevice/m.room.encrypted/'
).respond(200, function(path, content) {
var m = content.messages['@bob:xyz'].DEVICE_ID;
var ct = m.ciphertext[testSenderKey];
var decrypted = JSON.parse(p2pSession.decrypt(ct.type, ct.body));
expect(decrypted.type).toEqual('m.room_key');
inboundGroupSession = new Olm.InboundGroupSession();
inboundGroupSession.create(decrypted.content.session_key);
return {};
});
aliceTestClient.httpBackend.when(
'PUT', '/send/'
).respond(200, function(path, content) {
var ct = content.ciphertext;
var r = inboundGroupSession.decrypt(ct);
console.log('Decrypted received megolm message', r);
expect(r.message_index).toEqual(0);
var decrypted = JSON.parse(r.plaintext);
expect(decrypted.type).toEqual('m.room.message');
expect(decrypted.content.body).toEqual('test');
return {
event_id: '$event_id',
};
});
return aliceTestClient.start().then(function() {
var syncResponse = getSyncResponse(['@bob:xyz']);
// establish an olm session with alice
p2pSession = createOlmSession(testOlmAccount, aliceTestClient);
var olmEvent = encryptOlmEvent({
senderKey: testSenderKey,
recipient: aliceTestClient,
p2pSession: p2pSession,
});
syncResponse.to_device = { events: [olmEvent] };
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
return aliceTestClient.httpBackend.flush('/sync', 1);
}).then(function() {
console.log('Forcing alice to download our device keys');
// this will block
downloadPromise = aliceTestClient.client.downloadKeys(['@bob:xyz']);
}).then(function() {
// so will this.
sendPromise = aliceTestClient.client.sendTextMessage(ROOM_ID, 'test');
}).then(function() {
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(
200, getTestKeysQueryResponse('@bob:xyz')
);
return aliceTestClient.httpBackend.flush();
}).then(function() {
return q.all([downloadPromise, sendPromise]);
}).nodeify(done);
});
});
+1 -1
View File
@@ -28,7 +28,7 @@ module.exports.mock = function(constr, name) {
result.jasmineToString = function() {
return "mock" + (name ? " of " + name : "");
};
for (var key in constr.prototype) { // jshint ignore:line
for (var key in constr.prototype) { // eslint-disable-line guard-for-in
try {
if (constr.prototype[key] instanceof Function) {
result[key] = jasmine.createSpy((name || "mock") + '.' + key);
+1 -1
View File
@@ -6,7 +6,7 @@ describe("ContentRepo", function() {
var baseUrl = "https://my.home.server";
beforeEach(function() {
testUtils.beforeEach(this);
testUtils.beforeEach(this); // eslint-disable-line no-invalid-this
});
describe("getHttpUriForMxc", function() {
+1 -1
View File
@@ -9,6 +9,6 @@ describe("Crypto", function() {
}
it("Crypto exposes the correct olm library version", function() {
expect(Crypto.getOlmVersion()).toEqual([1, 3, 0]);
expect(Crypto.getOlmVersion()[0]).toEqual(2);
});
});
+1 -1
View File
@@ -15,7 +15,7 @@ describe("EventTimeline", function() {
var timeline;
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
// XXX: this is a horrid hack; should use sinon or something instead to mock
var timelineSet = { room: { roomId: roomId }};
+1 -1
View File
@@ -9,7 +9,7 @@ describe("Filter", function() {
var filter;
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
filter = new Filter(userId);
});
+1 -1
View File
@@ -24,7 +24,7 @@ var MatrixError = sdk.MatrixError;
describe("InteractiveAuth", function() {
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
});
it("should start an auth stage and complete it", function(done) {
+1 -1
View File
@@ -117,7 +117,7 @@ describe("MatrixClient", function() {
}
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
jasmine.Clock.useMock();
scheduler = jasmine.createSpyObj("scheduler", [
"getQueueForEvent", "queueEvent", "removeEventFromQueue",
+3 -3
View File
@@ -14,7 +14,7 @@ describe("realtime-callbacks", function() {
}
beforeEach(function() {
test_utils.beforeEach(this);
test_utils.beforeEach(this); // eslint-disable-line no-invalid-this
clock.useMock();
fakeDate = Date.now();
callbacks.setNow(function() { return fakeDate; });
@@ -54,8 +54,8 @@ describe("realtime-callbacks", function() {
it("should set 'this' to the global object", function() {
var callback = jasmine.createSpy();
callback.andCallFake(function() {
expect(this).toBe(global);
expect(this.console).toBeDefined();
expect(this).toBe(global); // eslint-disable-line no-invalid-this
expect(this.console).toBeDefined(); // eslint-disable-line no-invalid-this
});
callbacks.setTimeout(callback);
tick(0);
+1 -1
View File
@@ -11,7 +11,7 @@ describe("RoomMember", function() {
var member;
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
member = new RoomMember(roomId, userA);
});
+1 -1
View File
@@ -11,7 +11,7 @@ describe("RoomState", function() {
var state;
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
state = new RoomState(roomId);
state.setStateEvents([
utils.mkMembership({ // userA joined
+2 -2
View File
@@ -16,7 +16,7 @@ describe("Room", function() {
var room;
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
room = new Room(roomId);
// mock RoomStates
room.oldState = room.getLiveTimeline()._startState =
@@ -1110,7 +1110,7 @@ describe("Room", function() {
describe("tags", function() {
function mkTags(roomId, tags) {
var content = { "tags" : tags };
var content = { "tags": tags };
return new MatrixEvent({
content: content,
room_id: roomId,
+4 -2
View File
@@ -1,4 +1,6 @@
"use strict";
// This file had a function whose name is all caps, which displeases eslint
/* eslint new-cap: "off" */
var q = require("q");
var sdk = require("../..");
var MatrixScheduler = sdk.MatrixScheduler;
@@ -18,7 +20,7 @@ describe("MatrixScheduler", function() {
});
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
jasmine.Clock.useMock();
scheduler = new MatrixScheduler(function(ev, attempts, err) {
if (retryFn) {
+2 -2
View File
@@ -60,7 +60,7 @@ function createLinkedTimelines() {
describe("TimelineIndex", function() {
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
});
describe("minIndex", function() {
@@ -153,7 +153,7 @@ describe("TimelineWindow", function() {
}
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
});
describe("load", function() {
+1 -1
View File
@@ -8,7 +8,7 @@ describe("User", function() {
var user;
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
user = new User(userId);
});
+1 -1
View File
@@ -4,7 +4,7 @@ var testUtils = require("../test-utils");
describe("utils", function() {
beforeEach(function() {
testUtils.beforeEach(this);
testUtils.beforeEach(this); // eslint-disable-line no-invalid-this
});
describe("encodeParams", function() {
+1 -1
View File
@@ -33,7 +33,7 @@ describe("WebStorageStore", function() {
);
beforeEach(function() {
utils.beforeEach(this);
utils.beforeEach(this); // eslint-disable-line no-invalid-this
mockStorageApi = new MockStorageApi();
store = new WebStorageStore(mockStorageApi, batchNum);
room = new Room(roomId);
+51 -2
View File
@@ -45,6 +45,10 @@ var utils = require("./utils");
*
* @param {string} opts.accessToken The access_token for this user.
*
* @param {Number=} opts.localTimeoutMs Optional. The default maximum amount of
* time to wait before timing out HTTP requests. If not specified, there is no
* timeout.
*
* @param {Object} opts.queryParams Optional. Extra query parameters to append
* to all requests with this client. Useful for application services which require
* <code>?user_id=</code>.
@@ -63,7 +67,8 @@ function MatrixBaseApis(opts) {
request: opts.request,
prefix: httpApi.PREFIX_R0,
onlyData: true,
extraParams: opts.queryParams
extraParams: opts.queryParams,
localTimeoutMs: opts.localTimeoutMs
};
this._http = new httpApi.MatrixHttpApi(this, httpOpts);
@@ -434,6 +439,7 @@ MatrixBaseApis.prototype.roomInitialSync = function(roomId, limit, callback) {
// =========================
/**
* @param {Object} options Options for this request
* @param {string} options.server The remote server to query for the room list.
* Optional. If unspecified, get the local home
* server's public room list.
@@ -570,6 +576,30 @@ MatrixBaseApis.prototype.setRoomDirectoryVisibility =
);
};
/**
* Set the visbility of a room bridged to a 3rd party network in
* the current HS's room directory.
* @param {string} networkId the network ID of the 3rd party
* instance under which this room is published under.
* @param {string} roomId
* @param {string} visibility "public" to make the room visible
* in the public directory, or "private" to make
* it invisible.
* @param {module:client.callback} callback Optional.
* @return {module:client.Promise} Resolves: result object
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixBaseApis.prototype.setRoomDirectoryVisibilityAppService =
function(networkId, roomId, visibility, callback) {
var path = utils.encodeUri("/directory/list/appservice/$networkId/$roomId", {
$networkId: networkId,
$roomId: roomId
});
return this._http.authedRequest(
callback, "PUT", path, undefined, { "visibility": visibility }
);
};
// Media operations
// ================
@@ -688,6 +718,25 @@ MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) {
);
};
/**
* @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: The server response on success
* (generally the empty JSON object)
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixBaseApis.prototype.deleteThreePid = function(medium, address) {
var path = "/account/3pid/delete";
var data = {
'medium': medium,
'address': address
};
return this._http.authedRequestWithPrefix(
undefined, "POST", path, null, data, httpApi.PREFIX_UNSTABLE
);
};
/**
* Make a request to change your password.
* @param {Object} authDict
@@ -974,7 +1023,7 @@ MatrixBaseApis.prototype.downloadKeysForUsers = function(userIds, callback) {
/**
* Claim one-time keys
*
* @param {string[][]} devices a list of [userId, deviceId] pairs
* @param {string[]} devices a list of [userId, deviceId] pairs
*
* @param {string} [key_algorithm = signed_curve25519] desired key type
*
+45 -4
View File
@@ -94,6 +94,9 @@ try {
* to all requests with this client. Useful for application services which require
* <code>?user_id=</code>.
*
* @param {Number=} opts.localTimeoutMs Optional. The default maximum amount of
* time to wait before timing out HTTP requests. If not specified, there is no timeout.
*
* @param {boolean} [opts.timelineSupport = false] Set to true to enable
* improved timeline support ({@link
* module:client~MatrixClient#getEventTimeline getEventTimeline}). It is
@@ -354,8 +357,6 @@ MatrixClient.prototype.getStoredDevicesForUser = function(userId) {
return this._crypto.getStoredDevicesForUser(userId) || [];
};
/**
* Mark the given device as verified
*
@@ -374,7 +375,6 @@ MatrixClient.prototype.setDeviceVerified = function(userId, deviceId, verified)
_setDeviceVerification(this, userId, deviceId, verified, null);
};
/**
* Mark the given device as blocked/unblocked
*
@@ -539,7 +539,7 @@ MatrixClient.prototype.getUsers = function() {
/**
* Set account data event for the current user.
* @param {string} eventType The event type
* @param {Object} content the contents object for the event
* @param {Object} contents the contents object for the event
* @param {module:client.callback} callback Optional.
* @return {module:client.Promise} Resolves: TODO
* @return {module:http-api.MatrixError} Rejects: with an error response.
@@ -1472,6 +1472,47 @@ MatrixClient.prototype.setPresence = function(opts, callback) {
);
};
function _presenceList(callback, client, opts, method) {
var path = utils.encodeUri("/presence/list/$userId", {
$userId: client.credentials.userId
});
return client._http.authedRequest(callback, method, path, undefined, opts);
}
/**
* Retrieve current user presence list.
* @param {module:client.callback} callback Optional.
* @return {module:client.Promise} Resolves: TODO
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixClient.prototype.getPresenceList = function(callback) {
return _presenceList(callback, this, undefined, "GET");
};
/**
* Add users to the current user presence list.
* @param {module:client.callback} callback Optional.
* @param {string[]} userIds
* @return {module:client.Promise} Resolves: TODO
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixClient.prototype.inviteToPresenceList = function(callback, userIds) {
var opts = {"invite": userIds};
return _presenceList(callback, this, opts, "POST");
};
/**
* Drop users from the current user presence list.
* @param {module:client.callback} callback Optional.
* @param {string[]} userIds
* @return {module:client.Promise} Resolves: TODO
* @return {module:http-api.MatrixError} Rejects: with an error response.
**/
MatrixClient.prototype.dropFromPresenceList = function(callback, userIds) {
var opts = {"drop": userIds};
return _presenceList(callback, this, opts, "POST");
};
/**
* Retrieve older messages from the given room and put them in the timeline.
*
@@ -20,10 +20,33 @@ limitations under the License.
*
* @module crypto/OlmDevice
*/
var Olm = require("olm");
var utils = require("../utils");
// The maximum size of an event is 65K, and we base64 the content, so this is a
// reasonable approximation to the biggest plaintext we can encrypt.
var MAX_PLAINTEXT_LENGTH = 65536 * 3 / 4;
function checkPayloadLength(payloadString) {
if (payloadString === undefined) {
throw new Error("payloadString undefined");
}
if (payloadString.length > MAX_PLAINTEXT_LENGTH) {
// might as well fail early here rather than letting the olm library throw
// a cryptic memory allocation error.
//
// Note that even if we manage to do the encryption, the message send may fail,
// because by the time we've wrapped the ciphertext in the event object, it may
// exceed 65K. But at least we won't just fail with "abort()" in that case.
throw new Error("Message too long (" + payloadString.length + " bytes). " +
"The maximum for an encrypted message is " +
MAX_PLAINTEXT_LENGTH + " bytes.");
}
}
/**
* Manages the olm cryptography functions. Each OlmDevice has a single
* OlmAccount and a number of OlmSessions.
@@ -386,9 +409,7 @@ OlmDevice.prototype.encryptMessage = function(
) {
var self = this;
if (payloadString === undefined) {
throw new Error("payloadString undefined");
}
checkPayloadLength(payloadString);
return this._getSession(theirDeviceIdentityKey, sessionId, function(session) {
var res = session.encrypt(payloadString);
@@ -446,7 +467,6 @@ OlmDevice.prototype.matchesSession = function(
};
// Outbound group session
// ======================
@@ -515,6 +535,8 @@ OlmDevice.prototype.createOutboundGroupSession = function() {
OlmDevice.prototype.encryptGroupMessage = function(sessionId, payloadString) {
var self = this;
checkPayloadLength(payloadString);
return this._getOutboundGroupSession(sessionId, function(session) {
var res = session.encrypt(payloadString);
self._saveOutboundGroupSession(session);
@@ -130,7 +130,7 @@ module.exports.DecryptionAlgorithm = DecryptionAlgorithm;
*
* @method module:crypto/algorithms/base.DecryptionAlgorithm#onRoomKeyEvent
*
* @param {module:models/event.MatrixEvent} event key event
* @param {module:models/event.MatrixEvent} params event key event
*/
DecryptionAlgorithm.prototype.onRoomKeyEvent = function(params) {
// ignore by default
@@ -36,8 +36,6 @@ var base = require("./base");
* @property {string} sessionId
* @property {Number} useCount number of times this session has been used
* @property {Number} creationTime when the session was created (ms since the epoch)
* @property {module:client.Promise?} sharePromise If a share operation is in progress,
* a promise which resolves when it is complete.
*
* @property {object} sharedWithDevices
* devices with which we have shared the session key
@@ -47,7 +45,6 @@ function OutboundSessionInfo(sessionId) {
this.sessionId = sessionId;
this.useCount = 0;
this.creationTime = new Date().getTime();
this.sharePromise = null;
this.sharedWithDevices = {};
}
@@ -78,6 +75,45 @@ OutboundSessionInfo.prototype.needsRotation = function(
};
/**
* Determine if this session has been shared with devices which it shouldn't
* have been.
*
* @param {Object} devicesInRoom userId -> {deviceId -> object}
* devices we should shared the session with.
*
* @return {Boolean} true if we have shared the session with devices which aren't
* in devicesInRoom.
*/
OutboundSessionInfo.prototype.sharedWithTooManyDevices = function(
devicesInRoom
) {
for (var userId in this.sharedWithDevices) {
if (!this.sharedWithDevices.hasOwnProperty(userId)) { continue; }
if (!devicesInRoom.hasOwnProperty(userId)) {
console.log("Starting new session because we shared with " + userId);
return true;
}
for (var deviceId in this.sharedWithDevices[userId]) {
if (!this.sharedWithDevices[userId].hasOwnProperty(deviceId)) {
continue;
}
if (!devicesInRoom[userId].hasOwnProperty(deviceId)) {
console.log(
"Starting new session because we shared with " +
userId + ":" + deviceId
);
return true;
}
}
}
};
/**
* Megolm encryption implementation
*
@@ -90,10 +126,12 @@ OutboundSessionInfo.prototype.needsRotation = function(
function MegolmEncryption(params) {
base.EncryptionAlgorithm.call(this, params);
// OutboundSessionInfo. Null if we haven't yet started setting one up. Note
// that even if this is non-null, it may not be ready for use (in which
// case _outboundSession.sharePromise will be non-null.)
this._outboundSession = null;
// the most recent attempt to set up a session. This is used to serialise
// the session setups, so that we have a race-free view of which session we
// are using, and which devices we have shared the keys with. It resolves
// with an OutboundSessionInfo (or undefined, for the first message in the
// room).
this._setupPromise = q();
// default rotation periods
this._sessionRotationPeriodMsgs = 100;
@@ -112,30 +150,42 @@ utils.inherits(MegolmEncryption, base.EncryptionAlgorithm);
/**
* @private
*
* @param {module:models/room} room
* @param {Object} devicesInRoom The devices in this room, indexed by user ID
*
* @return {module:client.Promise} Promise which resolves to the
* OutboundSessionInfo when setup is complete.
*/
MegolmEncryption.prototype._ensureOutboundSession = function(room) {
MegolmEncryption.prototype._ensureOutboundSession = function(devicesInRoom) {
var self = this;
var session = this._outboundSession;
var session;
// need to make a brand new session?
if (!session || session.needsRotation(self._sessionRotationPeriodMsgs,
self._sessionRotationPeriodMs)
) {
this._outboundSession = session = this._prepareNewSession(room);
}
// takes the previous OutboundSessionInfo, and considers whether to create
// a new one. Also shares the key with any (new) devices in the room.
// Updates `session` to hold the final OutboundSessionInfo.
//
// returns a promise which resolves once the keyshare is successful.
function prepareSession(oldSession) {
session = oldSession;
if (session.sharePromise) {
// key share already in progress
return session.sharePromise;
}
// need to make a brand new session?
if (session && session.needsRotation(self._sessionRotationPeriodMsgs,
self._sessionRotationPeriodMs)
) {
console.log("Starting new megolm session because we need to rotate.");
session = null;
}
// no share in progress: check if we need to share with any devices
var prom = this._getDevicesInRoom(room).then(function(devicesInRoom) {
// determine if we have shared with anyone we shouldn't have
if (session && session.sharedWithTooManyDevices(devicesInRoom)) {
session = null;
}
if (!session) {
session = self._prepareNewSession();
}
// now check if we need to share with any devices
var shareMap = {};
for (var userId in devicesInRoom) {
@@ -152,10 +202,6 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) {
var deviceInfo = userDevices[deviceId];
if (deviceInfo.isBlocked()) {
continue;
}
var key = deviceInfo.getIdentityKey();
if (key == self._olmDevice.deviceCurve25519Key) {
// don't bother sending to ourself
@@ -175,24 +221,27 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) {
return self._shareKeyWithDevices(
session, shareMap
);
}).finally(function() {
session.sharePromise = null;
}).then(function() {
return session;
});
}
session.sharePromise = prom;
return prom;
// helper which returns the session prepared by prepareSession
function returnSession() { return session; }
// first wait for the previous share to complete
var prom = this._setupPromise.then(prepareSession);
// _setupPromise resolves to `session` whether or not the share succeeds
this._setupPromise = prom.then(returnSession, returnSession);
// but we return a promise which only resolves if the share was successful.
return prom.then(returnSession);
};
/**
* @private
*
* @param {module:models/room} room
*
* @return {module:crypto/algorithms/megolm.OutboundSessionInfo} session
*/
MegolmEncryption.prototype._prepareNewSession = function(room) {
MegolmEncryption.prototype._prepareNewSession = function() {
var session_id = this._olmDevice.createOutboundGroupSession();
var key = this._olmDevice.getOutboundGroupSessionKey(session_id);
@@ -329,13 +378,15 @@ MegolmEncryption.prototype._shareKeyWithDevices = function(session, devicesByUse
*
* @param {module:models/room} room
* @param {string} eventType
* @param {object} plaintext event content
* @param {object} content plaintext event content
*
* @return {module:client.Promise} Promise which resolves to the new event body
*/
MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
var self = this;
return this._ensureOutboundSession(room).then(function(session) {
return this._getDevicesInRoom(room).then(function(devicesInRoom) {
return self._ensureOutboundSession(devicesInRoom);
}).then(function(session) {
var payloadJson = {
room_id: self._roomId,
type: eventType,
@@ -362,30 +413,7 @@ MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
};
/**
* @inheritdoc
*
* @param {module:models/event.MatrixEvent} event event causing the change
* @param {module:models/room-member} member user whose membership changed
* @param {string=} oldMembership previous membership
*/
MegolmEncryption.prototype.onRoomMembership = function(event, member, oldMembership) {
var newMembership = member.membership;
if (newMembership === 'join' || newMembership === 'invite') {
return;
}
// otherwise we assume the user is leaving, and start a new outbound session.
console.log("Discarding outbound megolm session due to change in " +
"membership of " + member.userId + " (" + oldMembership +
"->" + newMembership + ")");
// this ensures that we will start a new session on the next message.
this._outboundSession = null;
};
/**
* Get the list of devices for all users in the room
* Get the list of unblocked devices for all users in the room
*
* @param {module:models/room} room
*
@@ -402,7 +430,26 @@ MegolmEncryption.prototype._getDevicesInRoom = function(room) {
// have a list of the user's devices, then we already share an e2e room
// with them, which means that they will have announced any new devices via
// an m.new_device.
return this._crypto.downloadKeys(roomMembers, false);
return this._crypto.downloadKeys(roomMembers, false).then(function(devices) {
// remove any blocked devices
for (var userId in devices) {
if (!devices.hasOwnProperty(userId)) {
continue;
}
var userDevices = devices[userId];
for (var deviceId in userDevices) {
if (!userDevices.hasOwnProperty(deviceId)) {
continue;
}
if (userDevices[deviceId].isBlocked()) {
delete userDevices[deviceId];
}
}
}
return devices;
});
};
/**
@@ -428,9 +475,6 @@ utils.inherits(MegolmDecryption, base.DecryptionAlgorithm);
*
* @param {MatrixEvent} event
*
* @return {null} The event referred to an unknown megolm session
* @return {module:crypto.DecryptionResult} decryption result
*
* @throws {module:crypto/algorithms/base.DecryptionError} if there is a
* problem decrypting the event
*/
@@ -79,7 +79,7 @@ OlmEncryption.prototype._ensureSession = function(roomMembers) {
*
* @param {module:models/room} room
* @param {string} eventType
* @param {object} plaintext event content
* @param {object} content plaintext event content
*
* @return {module:client.Promise} Promise which resolves to the new event body
*/
+90 -107
View File
@@ -55,8 +55,10 @@ function Crypto(baseApis, eventEmitter, sessionStore, userId, deviceId) {
this._deviceId = deviceId;
this._initialSyncCompleted = false;
// userId -> deviceId -> true
this._pendingNewDevices = {};
// userId -> true
this._pendingUsersWithNewDevices = {};
// userId -> [promise, ...]
this._keyDownloadsInProgressByUser = {};
this._olmDevice = new OlmDevice(sessionStore);
@@ -77,24 +79,32 @@ function Crypto(baseApis, eventEmitter, sessionStore, userId, deviceId) {
this._deviceKeys["curve25519:" + this._deviceId] =
this._olmDevice.deviceCurve25519Key;
// add our own deviceinfo to the sessionstore
var deviceInfo = {
keys: this._deviceKeys,
algorithms: this._supportedAlgorithms,
verified: DeviceVerification.VERIFIED,
};
var myDevices = this._sessionStore.getEndToEndDevicesForUser(
this._userId
) || {};
myDevices[this._deviceId] = deviceInfo;
this._sessionStore.storeEndToEndDevicesForUser(
this._userId, myDevices
);
_registerEventHandlers(this, eventEmitter);
if (!myDevices) {
// we don't yet have a list of our own devices; make sure we
// get one when we flush the pendingUsersWithNewDevices.
this._pendingUsersWithNewDevices[this._userId] = true;
myDevices = {};
}
// map from userId -> deviceId -> roomId -> timestamp
this._lastNewDeviceMessageTsByUserDeviceRoom = {};
if (!myDevices[this._deviceId]) {
// add our own deviceinfo to the sessionstore
var deviceInfo = {
keys: this._deviceKeys,
algorithms: this._supportedAlgorithms,
verified: DeviceVerification.VERIFIED,
};
myDevices[this._deviceId] = deviceInfo;
this._sessionStore.storeEndToEndDevicesForUser(
this._userId, myDevices
);
}
_registerEventHandlers(this, eventEmitter);
}
function _registerEventHandlers(crypto, eventEmitter) {
@@ -274,56 +284,71 @@ function _uploadOneTimeKeys(crypto) {
Crypto.prototype.downloadKeys = function(userIds, forceDownload) {
var self = this;
// map from userid -> deviceid -> DeviceInfo
var stored = {};
function storeDev(userId, dev) {
stored[userId][dev.deviceId] = dev;
}
// promises we need to wait for while the download happens
var promises = [];
// list of userids we need to download keys for
var downloadUsers = [];
function perUserCatch(u) {
return function(e) {
console.warn('Error downloading keys for user ' + u + ':', e);
};
}
if (forceDownload) {
downloadUsers = userIds;
} else {
for (var i = 0; i < userIds.length; ++i) {
var userId = userIds[i];
var devices = this.getStoredDevicesForUser(userId);
var u = userIds[i];
if (!devices) {
downloadUsers.push(userId);
} else {
stored[userId] = {};
devices.map(storeDev.bind(null, userId));
var inprogress = this._keyDownloadsInProgressByUser[u];
if (inprogress) {
// wait for the download to complete
promises.push(q.any(inprogress).catch(perUserCatch(u)));
} else if (!this.getStoredDevicesForUser(u)) {
downloadUsers.push(u);
}
}
}
if (downloadUsers.length === 0) {
return q(stored);
if (downloadUsers.length > 0) {
var r = this._doKeyDownloadForUsers(downloadUsers);
downloadUsers.map(function(u) {
promises.push(r[u].catch(perUserCatch(u)));
});
}
var r = this._doKeyDownloadForUsers(downloadUsers);
var promises = [];
downloadUsers.map(function(u) {
promises.push(r[u].catch(function(e) {
console.warn('Error downloading keys for user ' + u + ':', e);
}).then(function() {
stored[u] = {};
var devices = self.getStoredDevicesForUser(u) || [];
devices.map(storeDev.bind(null, u));
}));
});
return q.all(promises).then(function() {
return stored;
return self._getDevicesFromStore(userIds);
});
};
/**
* Get the stored device keys for a list of user ids
*
* @param {string[]} userIds the list of users to list keys for.
*
* @return {Object} userId->deviceId->{@link module:crypto/deviceinfo|DeviceInfo}.
*/
Crypto.prototype._getDevicesFromStore = function(userIds) {
var stored = {};
var self = this;
userIds.map(function(u) {
stored[u] = {};
var devices = self.getStoredDevicesForUser(u) || [];
devices.map(function(dev) {
stored[u][dev.deviceId] = dev;
});
});
return stored;
};
/**
* @param {string[]} downloadUsers list of userIds
*
* @return {Object a map from userId to a promise for a result for that user
* @return {Object} a map from userId to a promise for a result for that user
*/
Crypto.prototype._doKeyDownloadForUsers = function(downloadUsers) {
var self = this;
@@ -334,8 +359,23 @@ Crypto.prototype._doKeyDownloadForUsers = function(downloadUsers) {
var promiseMap = {};
downloadUsers.map(function(u) {
deferMap[u] = q.defer();
promiseMap[u] = deferMap[u].promise;
var deferred = q.defer();
var promise = deferred.promise.finally(function() {
var inProgress = self._keyDownloadsInProgressByUser[u];
utils.removeElement(inProgress, function(e) { return e === promise; });
if (inProgress.length === 0) {
// no more downloads for this user; remove the element
delete self._keyDownloadsInProgressByUser[u];
}
});
if (!self._keyDownloadsInProgressByUser[u]) {
self._keyDownloadsInProgressByUser[u] = [];
}
self._keyDownloadsInProgressByUser[u].push(promise);
deferMap[u] = deferred;
promiseMap[u] = promise;
});
this._baseApis.downloadKeysForUsers(
@@ -506,7 +546,7 @@ function _storeDeviceKeys(_olmDevice, userStore, deviceResult) {
*
* @param {string} userId the user to list keys for.
*
* @return {module:crypto/deviceinfo[]?} list of devices, or null if we haven't
* @return {module:crypto/deviceinfo[]|null} list of devices, or null if we haven't
* managed to get a list of devices for this user yet.
*/
Crypto.prototype.getStoredDevicesForUser = function(userId) {
@@ -919,58 +959,6 @@ Crypto.prototype.decryptEvent = function(event) {
alg.decryptEvent(event);
};
/**
* Send a "m.new_device" message to remind it that we exist and are a member
* of a room.
*
* This is rate limited to send a message at most once an hour per desination.
*
* @param {string} userId The ID of the user to ping.
* @param {string?} deviceId The ID of the device to ping. If null, all
* devices.
* @param {string} roomId The ID of the room we want to remind them about.
*/
Crypto.prototype._sendPingToDevice = function(userId, deviceId, roomId) {
if (deviceId === null) {
deviceId = "*";
}
var lastMessageTsMap = this._lastNewDeviceMessageTsByUserDeviceRoom;
var lastTsByDevice = lastMessageTsMap[userId];
if (!lastTsByDevice) {
lastTsByDevice = lastMessageTsMap[userId] = {};
}
var lastTsByRoom = lastTsByDevice[deviceId];
if (!lastTsByRoom) {
lastTsByRoom = lastTsByDevice[deviceId] = {};
}
var lastTs = lastTsByRoom[roomId];
var timeNowMs = Date.now();
var oneHourMs = 1000 * 60 * 60;
if (lastTs !== undefined && lastTs + oneHourMs > timeNowMs) {
// rate-limiting
return;
}
var content = {};
content[userId] = {};
content[userId][deviceId] = {
device_id: this._deviceId,
rooms: [roomId],
};
this._baseApis.sendToDevice(
"m.new_device", // OH HAI!
content
).done();
lastTsByRoom[roomId] = timeNowMs;
};
/**
* handle an m.room.encryption event
*
@@ -1134,8 +1122,7 @@ Crypto.prototype._onNewDeviceEvent = function(event) {
return;
}
this._pendingNewDevices[userId] = this._pendingNewDevices[userId] || {};
this._pendingNewDevices[userId][deviceId] = true;
this._pendingUsersWithNewDevices[userId] = true;
// we delay handling these until the intialsync has completed, so that we
// can do all of them together.
@@ -1150,10 +1137,7 @@ Crypto.prototype._onNewDeviceEvent = function(event) {
Crypto.prototype._flushNewDeviceRequests = function() {
var self = this;
var pending = this._pendingNewDevices;
var users = utils.keys(pending).filter(function(u) {
return utils.keys(pending[u]).length > 0;
});
var users = utils.keys(this._pendingUsersWithNewDevices);
if (users.length === 0) {
return;
@@ -1163,7 +1147,7 @@ Crypto.prototype._flushNewDeviceRequests = function() {
// we've kicked off requests to these users: remove their
// pending flag for now.
this._pendingNewDevices = {};
this._pendingUsersWithNewDevices = {};
users.map(function(u) {
r[u] = r[u].catch(function(e) {
@@ -1175,8 +1159,7 @@ Crypto.prototype._flushNewDeviceRequests = function() {
// mean that we will do another download in the future, but won't
// tight-loop.
//
self._pendingNewDevices[u] = self._pendingNewDevices[u] || {};
utils.update(self._pendingNewDevices[u], pending[u]);
self._pendingUsersWithNewDevices[u] = true;
});
});
@@ -159,9 +159,10 @@ module.exports.ensureOlmSessionsForDevices = function(
return baseApis.claimOneTimeKeys(
devicesWithoutSession, oneTimeKeyAlgorithm
).then(function(res) {
var otk_res = res.one_time_keys || {};
for (var userId in devicesByUser) {
if (!devicesByUser.hasOwnProperty(userId)) { continue; }
var userRes = res.one_time_keys[userId] || {};
var userRes = otk_res[userId] || {};
var devices = devicesByUser[userId];
for (var j = 0; j < devices.length; j++) {
var deviceInfo = devices[j];
@@ -44,7 +44,7 @@ function _matches_wildcard(actual_value, filter_value) {
* 'Filters' are referred to as 'FilterCollections'.
*
* @constructor
* @param {Object} the definition of this filter JSON, e.g. { 'contains_url': true }
* @param {Object} filter_json the definition of this filter JSON, e.g. { 'contains_url': true }
*/
function FilterComponent(filter_json) {
this.filter_json = filter_json;
View File
+5 -3
View File
@@ -70,8 +70,10 @@ module.exports.PREFIX_MEDIA_R0 = "/_matrix/media/r0";
*
* @param {string} opts.accessToken The access_token to send with requests. Can be
* null to not send an access token.
* @param {Object} opts.extraParams Optional. Extra query parameters to send on
* @param {Object=} opts.extraParams Optional. Extra query parameters to send on
* requests.
* @param {Number=} opts.localTimeoutMs The default maximum amount of time to wait
* before timing out the request. If not specified, there is no timeout.
*/
module.exports.MatrixHttpApi = function MatrixHttpApi(event_emitter, opts) {
utils.checkObjectHasKeys(opts, ["baseUrl", "request", "prefix"]);
@@ -594,7 +596,7 @@ module.exports.MatrixHttpApi.prototype = {
* @param {object=} opts.headers extra request headers
*
* @param {number=} opts.localTimeoutMs client-side timeout for the
* request. No timeout if undefined.
* request. Default timeout if falsy.
*
* @param {function=} opts.bodyParser function to parse the body of the
* response before passing it to the promise and callback.
@@ -626,7 +628,7 @@ module.exports.MatrixHttpApi.prototype = {
var timeoutId;
var timedOut = false;
var req;
var localTimeoutMs = opts.localTimeoutMs;
var localTimeoutMs = opts.localTimeoutMs || this.opts.localTimeoutMs;
if (localTimeoutMs) {
timeoutId = callbacks.setTimeout(function() {
timedOut = true;
@@ -89,8 +89,8 @@ InteractiveAuth.prototype = {
/**
* get the server params for a given stage
*
* @param {string} login type for the stage
* @return {object?} any parameters from the server for this stage
* @param {string} loginType login type for the stage
* @return {object?} any parameters from the server for this stage
*/
getStageParams: function(loginType) {
var params = {};
View File
@@ -24,11 +24,12 @@ var EventTimeline = require("./event-timeline");
// var DEBUG = false;
var DEBUG = true;
var debuglog;
if (DEBUG) {
// using bind means that we get to keep useful line numbers in the console
var debuglog = console.log.bind(console);
debuglog = console.log.bind(console);
} else {
var debuglog = function() {};
debuglog = function() {};
}
/**
+21 -2
View File
@@ -78,6 +78,8 @@ module.exports.MatrixEvent = function MatrixEvent(
this.status = null;
this.forwardLooking = true;
this._pushActions = null;
this._date = this.event.origin_server_ts ?
new Date(this.event.origin_server_ts) : null;
this._clearEvent = {};
this._keysProved = {};
@@ -142,6 +144,14 @@ utils.extend(module.exports.MatrixEvent.prototype, {
return this.event.origin_server_ts;
},
/**
* Get the timestamp of this event, as a Date object.
* @return {Date} The event date, e.g. <code>new Date(1433502692297)</code>
*/
getDate: function() {
return this._date;
},
/**
* Get the (decrypted, if necessary) event content JSON.
*
@@ -374,6 +384,17 @@ utils.extend(module.exports.MatrixEvent.prototype, {
setPushActions: function(pushActions) {
this._pushActions = pushActions;
},
/**
* Replace the `event` property and recalculate any properties based on it.
* @param {Object} event the object to assign to the `event` property
*/
handleRemoteEcho: function(event) {
this.event = event;
// successfully sent.
this.status = null;
this._date = new Date(this.event.origin_server_ts);
}
});
@@ -417,8 +438,6 @@ var _REDACT_KEEP_CONTENT_MAP = {
};
/**
* Fires when an event is decrypted
*
@@ -253,7 +253,7 @@ RoomState.prototype.maySendMessage = function(userId) {
/**
* Returns true if the given user ID has permission to send a normal
* event of type `eventType` into this room.
* @param {string} type The type of event to test
* @param {string} eventType The type of event to test
* @param {string} userId The user ID of the user to test permission for
* @return {boolean} true if the given user ID should be permitted to send
* the given type of event into this room,
@@ -267,8 +267,8 @@ RoomState.prototype.maySendEvent = function(eventType, userId) {
/**
* Returns true if the given MatrixClient has permission to send a state
* event of type `stateEventType` into this room.
* @param {string} type The type of state events to test
* @param {MatrixClient} The client to test permission for
* @param {string} stateEventType The type of state events to test
* @param {MatrixClient} cli The client to test permission for
* @return {boolean} true if the given client should be permitted to send
* the given type of state event into this room,
* according to the room's state.
@@ -283,7 +283,7 @@ RoomState.prototype.mayClientSendStateEvent = function(stateEventType, cli) {
/**
* Returns true if the given user ID has permission to send a state
* event of type `stateEventType` into this room.
* @param {string} type The type of state events to test
* @param {string} stateEventType The type of state events to test
* @param {string} userId The user ID of the user to test permission for
* @return {boolean} true if the given user ID should be permitted to send
* the given type of state event into this room,
@@ -296,7 +296,7 @@ RoomState.prototype.maySendStateEvent = function(stateEventType, userId) {
/**
* Returns true if the given user ID has permission to send a normal or state
* event of type `eventType` into this room.
* @param {string} type The type of event to test
* @param {string} eventType The type of event to test
* @param {string} userId The user ID of the user to test permission for
* @param {boolean} state If true, tests if the user may send a state
event of this type. Otherwise tests whether
@@ -314,18 +314,12 @@ RoomState.prototype._maySendEventOfType = function(eventType, userId, state) {
var power_levels;
var events_levels = {};
var default_user_level = 0;
var user_levels = [];
var state_default = 0;
var events_default = 0;
if (power_levels_event) {
power_levels = power_levels_event.getContent();
events_levels = power_levels.events || {};
default_user_level = parseInt(power_levels.users_default || 0);
user_levels = power_levels.users || {};
if (power_levels.state_default !== undefined) {
state_default = power_levels.state_default;
} else {
+1 -6
View File
@@ -674,10 +674,7 @@ Room.prototype._handleRemoteEcho = function(remoteEvent, localEvent) {
// replace the event source (this will preserve the plaintext payload if
// any, which is good, because we don't want to try decoding it again).
localEvent.event = remoteEvent.event;
// successfully sent.
localEvent.status = null;
localEvent.handleRemoteEcho(remoteEvent.event);
for (var i = 0; i < this._timelineSets.length; i++) {
var timelineSet = this._timelineSets[i];
@@ -909,8 +906,6 @@ Room.prototype.recalculate = function(userId) {
});
}
var oldName = this.name;
this.name = calculateRoomName(this, userId);
this.summary = new RoomSummary(this.roomId, {
@@ -92,7 +92,7 @@ WebStorageSessionStore.prototype = {
* @param {string} userId The user's ID.
* @return {object} A map from device ID to keys for the device.
*/
getEndToEndDevicesForUser: function(userId) {
getEndToEndDevicesForUser: function(userId) {
return getJsonItem(this.store, keyEndToEndDevicesForUser(userId));
},
+1 -1
View File
@@ -1016,7 +1016,7 @@ SyncApi.prototype._resolveInvites = function(room) {
* @param {Room} room
* @param {MatrixEvent[]} stateEventList A list of state events. This is the state
* at the *START* of the timeline list if it is supplied.
* @param {?MatrixEvent[]} timelineEventList A list of timeline events. Lower index
* @param {MatrixEvent[]} [timelineEventList] A list of timeline events. Lower index
* is earlier in time. Higher index is later.
*/
SyncApi.prototype._processRoomEvents = function(room, stateEventList,
+1 -5
View File
@@ -348,16 +348,12 @@ var deepCompare = module.exports.deepCompare = function(x, y) {
*/
module.exports.extend = function() {
var target = arguments[0] || {};
// disable jshint "The body of a for in should be wrapped in an if
// statement"
/* jshint -W089 */
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var propName in source) {
for (var propName in source) { // eslint-disable-line guard-for-in
target[propName] = source[propName];
}
}
/* jshint +W089 */
return target;
};
+8 -17
View File
@@ -153,7 +153,7 @@ MatrixCall.prototype.placeScreenSharingCall =
/**
* Play the given HTMLMediaElement, serialising the operation into a chain
* of promises to avoid racing access to the element
* @param {Element} HTMLMediaElement element to play
* @param {Element} element HTMLMediaElement element to play
* @param {string} queueId Arbitrary ID to track the chain of promises to be used
*/
MatrixCall.prototype.playElement = function(element, queueId) {
@@ -183,7 +183,7 @@ MatrixCall.prototype.playElement = function(element, queueId) {
/**
* Pause the given HTMLMediaElement, serialising the operation into a chain
* of promises to avoid racing access to the element
* @param {Element} HTMLMediaElement element to pause
* @param {Element} element HTMLMediaElement element to pause
* @param {string} queueId Arbitrary ID to track the chain of promises to be used
*/
MatrixCall.prototype.pauseElement = function(element, queueId) {
@@ -209,7 +209,7 @@ MatrixCall.prototype.pauseElement = function(element, queueId) {
* Assign the given HTMLMediaElement by setting the .src attribute on it,
* serialising the operation into a chain of promises to avoid racing access
* to the element
* @param {Element} HTMLMediaElement element to pause
* @param {Element} element HTMLMediaElement element to pause
* @param {string} src the src attribute value to assign to the element
* @param {string} queueId Arbitrary ID to track the chain of promises to be used
*/
@@ -522,17 +522,16 @@ MatrixCall.prototype._gotUserMediaForInvite = function(stream) {
}, 0);
}
if (this.screenSharingStream) {
this.screenSharingStream.addTrack(stream.getAudioTracks()[0]);
stream = this.screenSharingStream;
}
this.localAVStream = stream;
// why do we enable audio (and only audio) tracks here? -- matthew
setTracksEnabled(stream.getAudioTracks(), true);
this.peerConn = _createPeerConnection(this);
this.peerConn.addStream(stream);
if (this.screenSharingStream) {
console.log("Adding screen-sharing stream to peer connection");
this.peerConn.addStream(this.screenSharingStream);
// let's use this for the local preview...
this.localAVStream = this.screenSharingStream;
}
this.peerConn.createOffer(
hookCallback(self, self._gotLocalOffer),
hookCallback(self, self._getLocalOfferFailed)
@@ -1144,14 +1143,6 @@ var _getChromeScreenSharingConstraints = function(call) {
));
return;
}
// it won't work at all if you're not on HTTPS so whine whine whine
if (!global.window || global.window.location.protocol !== "https:") {
call.emit("error", callError(
MatrixCall.ERR_NO_USER_MEDIA,
"You need to be using HTTPS to place a screen-sharing call."
));
return;
}
return {
video: {