diff --git a/lib/crypto/index.js b/lib/crypto/index.js index 9bfeb3b23..035c81d22 100644 --- a/lib/crypto/index.js +++ b/lib/crypto/index.js @@ -55,8 +55,8 @@ function Crypto(baseApis, eventEmitter, sessionStore, userId, deviceId) { this._deviceId = deviceId; this._initialSyncCompleted = false; - // userId -> deviceId -> true - this._pendingNewDevices = {}; + // userId -> true + this._pendingUsersWithNewDevices = {}; this._olmDevice = new OlmDevice(sessionStore); @@ -77,20 +77,31 @@ 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 ); + 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 = {}; + } + + 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); // map from userId -> deviceId -> roomId -> timestamp @@ -1134,8 +1145,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 +1160,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 +1170,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 +1182,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; }); }); diff --git a/spec/integ/matrix-client-crypto.spec.js b/spec/integ/matrix-client-crypto.spec.js index b563a08c8..9d7cbbd5e 100644 --- a/spec/integ/matrix-client-crypto.spec.js +++ b/spec/integ/matrix-client-crypto.spec.js @@ -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"); diff --git a/spec/integ/megolm.spec.js b/spec/integ/megolm.spec.js index 086180fe7..5a8fa5d38 100644 --- a/spec/integ/megolm.spec.js +++ b/spec/integ/megolm.spec.js @@ -59,12 +59,25 @@ function TestClient(userId, deviceId, accessToken) { /** * start the client, and wait for it to initialise. * + * @param {object?} deviceQueryResponse 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();