From 9a53fa3876e5ec86321f0c405e9f83090a998d99 Mon Sep 17 00:00:00 2001 From: Johannes Bornhold Date: Sun, 31 Dec 2017 01:24:27 +0100 Subject: [PATCH 01/17] Fix typo around getThirdpartyLocation --- src/base-apis.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base-apis.js b/src/base-apis.js index 3ae681917..2b8578d2d 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1668,7 +1668,7 @@ MatrixBaseApis.prototype.getThirdpartyProtocols = function() { * Get information on how a specific place on a third party protocol * may be reached. * @param {string} protocol The protocol given in getThirdpartyProtocols() - * @param {object} params Protocol-specific parameters, as given in th + * @param {object} params Protocol-specific parameters, as given in the * response to getThirdpartyProtocols() * @return {module:client.Promise} Resolves to the result object */ From 81de2b3afc47b7157eed358d92805c1453474148 Mon Sep 17 00:00:00 2001 From: Johannes Bornhold Date: Sun, 31 Dec 2017 01:24:55 +0100 Subject: [PATCH 02/17] Add getThirdpartyUser Signed-off-by: Johannes Bornhold --- src/base-apis.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/base-apis.js b/src/base-apis.js index 2b8578d2d..b8f6a156f 100644 --- a/src/base-apis.js +++ b/src/base-apis.js @@ -1683,6 +1683,25 @@ MatrixBaseApis.prototype.getThirdpartyLocation = function(protocol, params) { ); }; +/** + * Get information on how a specific user on a third party protocol + * may be reached. + * @param {string} protocol The protocol given in getThirdpartyProtocols() + * @param {object} params Protocol-specific parameters, as given in the + * response to getThirdpartyProtocols() + * @return {module:client.Promise} Resolves to the result object + */ +MatrixBaseApis.prototype.getThirdpartyUser = function(protocol, params) { + const path = utils.encodeUri("/thirdparty/user/$protocol", { + $protocol: protocol, + }); + + return this._http.authedRequestWithPrefix( + undefined, "GET", path, params, undefined, + httpApi.PREFIX_UNSTABLE, + ); +}; + /** * MatrixBaseApis object */ From 9cdcbf6bf8ce6f7e1327aa565875b17d57b0a52e Mon Sep 17 00:00:00 2001 From: Leon Date: Tue, 22 May 2018 11:27:05 +0800 Subject: [PATCH 03/17] emit oldEventId on "updatePendingEvent" It should fire `Room.localEchoUpdated` event and returns the oldEventId --- src/models/room.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/room.js b/src/models/room.js index 110b08c39..20a2e95b9 100644 --- a/src/models/room.js +++ b/src/models/room.js @@ -817,7 +817,7 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) { this.removeEvent(oldEventId); } - this.emit("Room.localEchoUpdated", event, this, event.getId(), oldStatus); + this.emit("Room.localEchoUpdated", event, this, oldEventId, oldStatus); }; From b34716f7e9f40ce2b26e2750af20dfafcd91ea8e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 26 Jul 2018 14:50:55 +0100 Subject: [PATCH 04/17] take into account homoglyphs when calculating similar display names to prevent homoglyph attacks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 3 ++- src/utils.js | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2796f502d..63809fa60 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,8 @@ "bluebird": "^3.5.0", "browser-request": "^0.3.3", "content-type": "^1.0.2", - "request": "^2.53.0" + "request": "^2.53.0", + "unhomoglyph": "^1.0.2" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/src/utils.js b/src/utils.js index 1587c64a3..0f05d6eb3 100644 --- a/src/utils.js +++ b/src/utils.js @@ -19,6 +19,8 @@ limitations under the License. * @module utils */ +const unhomoglyph = require('unhomoglyph'); + /** * Encode a dictionary of query parameters. * @param {Object} params A dict of key/values to encode e.g. @@ -665,10 +667,11 @@ module.exports.isNumber = function(value) { /** * Removes zero width chars, diacritics and whitespace from the string + * Also applies an unhomoglyph on the string, to prevent similar looking chars * @param {string} str the string to remove hidden characters from * @return {string} a string with the hidden characters removed */ module.exports.removeHiddenChars = function(str) { - return str.normalize('NFD').replace(removeHiddenCharsRegex, ''); + return unhomoglyph(str.normalize('NFD').replace(removeHiddenCharsRegex, '')); }; const removeHiddenCharsRegex = /[\u200B-\u200D\u0300-\u036f\uFEFF\s]/g; From 848e6e58975f1c39fae2050631846fbf3ae7bed0 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 11 Dec 2018 21:41:15 -0700 Subject: [PATCH 05/17] Support reading custom status messages Part of https://github.com/vector-im/riot-web/issues/1528 --- src/models/room-state.js | 10 ++++++++++ src/models/user.js | 14 ++++++++++++++ src/sync.js | 10 ++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/models/room-state.js b/src/models/room-state.js index ac5e20077..3dbd7ff88 100644 --- a/src/models/room-state.js +++ b/src/models/room-state.js @@ -154,6 +154,16 @@ RoomState.prototype.getMembers = function() { return utils.values(this.members); }; +/** + * Get all RoomMembers in this room, excluding the user IDs provided. + * @param {Array} excludedIds The user IDs to exclude. + * @return {Array} A list of RoomMembers. + */ +RoomState.prototype.getMembersExcept = function(excludedIds) { + return utils.values(this.members) + .filter((m) => !excludedIds.includes(m.userId)); +}; + /** * Get a room member by their user ID. * @param {string} userId The room member's user ID. diff --git a/src/models/user.js b/src/models/user.js index dbfaeb791..f4b5f7a26 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -39,6 +39,9 @@ limitations under the License. * when a user was last active. * @prop {Boolean} currentlyActive Whether we should consider lastActiveAgo to be * an approximation and that the user should be seen as active 'now' + * @prop {string} statusMessage The status message for the user, if known. This is + * different from the presenceStatusMsg in that this is not tied to + * the user's presence, and should be represented differently. * @prop {Object} events The events describing this user. * @prop {MatrixEvent} events.presence The m.presence event for this user. */ @@ -46,6 +49,7 @@ function User(userId) { this.userId = userId; this.presence = "offline"; this.presenceStatusMsg = null; + this.statusMessage = ""; this.displayName = userId; this.rawDisplayName = userId; this.avatarUrl = null; @@ -179,6 +183,16 @@ User.prototype.getLastActiveTs = function() { return this.lastPresenceTs - this.lastActiveAgo; }; +/** + * Manually set the user's status message. + * @param {MatrixEvent} event The im.vector.user_status event. + */ +User.prototype.updateStatusMessage = function(event) { + if (!event.getContent()) this.statusMessage = ""; + else this.statusMessage = event.getContent()["status"]; + this._updateModifiedTime(); +}; + /** * The User class. */ diff --git a/src/sync.js b/src/sync.js index 1ce75ba82..9d8c9a571 100644 --- a/src/sync.js +++ b/src/sync.js @@ -1172,6 +1172,16 @@ SyncApi.prototype._processSyncResponse = async function( if (e.isState() && e.getType() == "m.room.encryption" && self.opts.crypto) { await self.opts.crypto.onCryptoEvent(e); } + if (e.isState() && e.getType() === "im.vector.user_status") { + let user = client.store.getUser(e.getStateKey()); + if (user) { + user.updateStatusMessage(e); + } else { + user = createNewUser(client, e.getStateKey()); + user.updateStatusMessage(e); + client.store.storeUser(user); + } + } } await Promise.mapSeries(stateEvents, processRoomEvent); From fb65c7f4bab0ad3764657b3f4c67ab93093343f6 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 12 Dec 2018 13:21:13 -0700 Subject: [PATCH 06/17] Support setting status message in rooms that look like 1:1s Part of https://github.com/vector-im/riot-web/issues/1528 --- src/client.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/client.js b/src/client.js index 22cae3e87..9b5726f2f 100644 --- a/src/client.js +++ b/src/client.js @@ -2262,6 +2262,27 @@ MatrixClient.prototype.mxcUrlToHttp = ); }; +/** + * Sets a new status message for the user. The message may be null/falsey + * to clear the message. + * @param {string} newMessage The new message to set. + * @return {module:client.Promise} Resolves: to nothing + * @return {module:http-api.MatrixError} Rejects: with an error response. + */ +MatrixClient.prototype.setStatusMessage = function(newMessage) { + return Promise.all(this.getRooms().map((room) => { + const isJoined = room.getMyMembership() === "join"; + const looksLikeDm = room.getInvitedAndJoinedMemberCount() === 2; + if (isJoined && looksLikeDm) { + return this.sendStateEvent(room.roomId, "im.vector.user_status", { + status: newMessage, + }, this.getUserId()); + } else { + return Promise.resolve(); + } + })); +}; + /** * @param {Object} opts Options to apply * @param {string} opts.presence One of "online", "offline" or "unavailable" From 08b3dfa3b5b5d0b63272f0b80b9fdd88d0795c45 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 12 Dec 2018 23:05:03 -0700 Subject: [PATCH 07/17] Prefix the status message API with `_unstable` It's not a formal feature of Matrix yet, so we should try and avoid people relying on it. This makes it appear as a private API and is very clearly labeled as not intended for use. --- src/client.js | 2 +- src/models/user.js | 8 ++++---- src/sync.js | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/client.js b/src/client.js index 9b5726f2f..c942acb9d 100644 --- a/src/client.js +++ b/src/client.js @@ -2269,7 +2269,7 @@ MatrixClient.prototype.mxcUrlToHttp = * @return {module:client.Promise} Resolves: to nothing * @return {module:http-api.MatrixError} Rejects: with an error response. */ -MatrixClient.prototype.setStatusMessage = function(newMessage) { +MatrixClient.prototype._unstable_setStatusMessage = function(newMessage) { return Promise.all(this.getRooms().map((room) => { const isJoined = room.getMyMembership() === "join"; const looksLikeDm = room.getInvitedAndJoinedMemberCount() === 2; diff --git a/src/models/user.js b/src/models/user.js index f4b5f7a26..d1496c996 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -49,7 +49,7 @@ function User(userId) { this.userId = userId; this.presence = "offline"; this.presenceStatusMsg = null; - this.statusMessage = ""; + this._unstable_statusMessage = ""; this.displayName = userId; this.rawDisplayName = userId; this.avatarUrl = null; @@ -187,9 +187,9 @@ User.prototype.getLastActiveTs = function() { * Manually set the user's status message. * @param {MatrixEvent} event The im.vector.user_status event. */ -User.prototype.updateStatusMessage = function(event) { - if (!event.getContent()) this.statusMessage = ""; - else this.statusMessage = event.getContent()["status"]; +User.prototype._unstable_updateStatusMessage = function(event) { + if (!event.getContent()) this._unstable_statusMessage = ""; + else this._unstable_statusMessage = event.getContent()["status"]; this._updateModifiedTime(); }; diff --git a/src/sync.js b/src/sync.js index 9d8c9a571..f4ea0bf88 100644 --- a/src/sync.js +++ b/src/sync.js @@ -1175,10 +1175,10 @@ SyncApi.prototype._processSyncResponse = async function( if (e.isState() && e.getType() === "im.vector.user_status") { let user = client.store.getUser(e.getStateKey()); if (user) { - user.updateStatusMessage(e); + user._unstable_updateStatusMessage(e); } else { user = createNewUser(client, e.getStateKey()); - user.updateStatusMessage(e); + user._unstable_updateStatusMessage(e); client.store.storeUser(user); } } From 06bc6e7568403cf9d391636fa09d3256649ed918 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 13 Dec 2018 09:42:22 -0700 Subject: [PATCH 08/17] Update jsdoc to match _unstable_ prefix --- src/models/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/user.js b/src/models/user.js index d1496c996..ec2f495af 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -39,7 +39,7 @@ limitations under the License. * when a user was last active. * @prop {Boolean} currentlyActive Whether we should consider lastActiveAgo to be * an approximation and that the user should be seen as active 'now' - * @prop {string} statusMessage The status message for the user, if known. This is + * @prop {string} _unstable_statusMessage The status message for the user, if known. This is * different from the presenceStatusMsg in that this is not tied to * the user's presence, and should be represented differently. * @prop {Object} events The events describing this user. From 66cdb62a3d4f97e17589d05e21e2fbcdd743e897 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 12 Dec 2018 16:13:48 +0000 Subject: [PATCH 09/17] Reorganize key backup flow This will allow the key backup flow to propagate errors for things like version mismatches more easily. In addition, it raises the limit of keys sent per request from 10 to 200 to cut down on the number of requests. --- src/crypto/index.js | 180 ++++++++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 80 deletions(-) diff --git a/src/crypto/index.js b/src/crypto/index.js index 17abf8933..a530f193e 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -42,6 +42,7 @@ export function isCryptoAvailable() { } const MIN_FORCE_SESSION_INTERVAL_MS = 60 * 60 * 1000; +const KEY_BACKUP_KEYS_PER_REQUEST = 200; /** * Cryptography bits @@ -986,96 +987,110 @@ Crypto.prototype.importRoomKeys = function(keys) { ); }; -Crypto.prototype._maybeSendKeyBackup = async function(delay, retry) { - if (retry === undefined) retry = true; +/** + * Schedules sending all keys waiting to be sent to the backup, if not already + * scheduled. Retries if necessary. + */ +Crypto.prototype._scheduleKeyBackupSend = async function() { + if (this._sendingBackups) return; - if (!this._sendingBackups) { - this._sendingBackups = true; - try { - if (delay === undefined) { - // by default, wait between 0 and 10 seconds, to avoid backup - // requests from different clients hitting the server all at - // the same time when a new key is sent - delay = Math.random() * 10000; + try { + // wait between 0 and 10 seconds, to avoid backup + // requests from different clients hitting the server all at + // the same time when a new key is sent + const delay = Math.random() * 10000; + await Promise.delay(delay); + let numFailures = 0; // number of consecutive failures + while (1) { + if (!this.backupKey) { + return; } - await Promise.delay(delay); - let numFailures = 0; // number of consecutive failures - while (1) { - if (!this.backupKey) { + try { + const numBackedUp = + await this._backupPendingKeys(KEY_BACKUP_KEYS_PER_REQUEST); + if (numBackedUp === 0) { + // no sessions left needing backup: we're done return; } - // FIXME: figure out what limit is reasonable - const sessions = await this._cryptoStore.getSessionsNeedingBackup(10); - if (!sessions.length) { - return; - } - const data = {}; - for (const session of sessions) { - const roomId = session.sessionData.room_id; - if (data[roomId] === undefined) { - data[roomId] = {sessions: {}}; - } - - const sessionData = await this._olmDevice.exportInboundGroupSession( - session.senderKey, session.sessionId, session.sessionData, - ); - sessionData.algorithm = olmlib.MEGOLM_ALGORITHM; - delete sessionData.session_id; - delete sessionData.room_id; - const firstKnownIndex = sessionData.first_known_index; - delete sessionData.first_known_index; - const encrypted = this.backupKey.encrypt(JSON.stringify(sessionData)); - - const forwardedCount = - (sessionData.forwarding_curve25519_key_chain || []).length; - - const device = this._deviceList.getDeviceByIdentityKey( - olmlib.MEGOLM_ALGORITHM, session.senderKey, - ); - - data[roomId]['sessions'][session.sessionId] = { - first_message_index: firstKnownIndex, - forwarded_count: forwardedCount, - is_verified: !!(device && device.isVerified()), - session_data: encrypted, - }; - } - - try { - await this._baseApis.sendKeyBackup( - undefined, undefined, this.backupInfo.version, - {rooms: data}, - ); - numFailures = 0; - await this._cryptoStore.unmarkSessionsNeedingBackup(sessions); - } catch (err) { - numFailures++; - console.log("send failed", err); - if (err.httpStatus === 400 - || err.httpStatus === 403 - || err.httpStatus === 401 - || !retry) { - // retrying probably won't help much, so we should give up - // FIXME: disable backups? + numFailures = 0; + } catch (err) { + numFailures++; + console.log("Key backup request failed", err); + if (err.data) { + if ( + err.data.errcode == 'M_NOT_FOUND' || + err.data.errcode == 'M_WRONG_ROOM_KEYS_VERSION' + ) { + // Backup version has changed or this backup version + // has been deleted throw err; } } - if (numFailures) { - // exponential backoff if we have failures - await new Promise((resolve, reject) => { - setTimeout( - resolve, - 1000 * Math.pow(2, Math.min(numFailures - 1, 4)), - ); - }); - } } - } finally { - this._sendingBackups = false; + if (numFailures) { + // exponential backoff if we have failures + await Promise.delay(1000 * Math.pow(2, Math.min(numFailures - 1, 4))); + } } + } finally { + this._sendingBackups = false; } }; +/** + * Take some e2e keys waiting to be backed up and send them + * to the backup. + * + * @param {integer} limit Maximum number of keys to back up + * @returns {integer} Number of sessions backed up + */ +Crypto.prototype._backupPendingKeys = async function(limit) { + const sessions = await this._cryptoStore.getSessionsNeedingBackup(limit); + if (!sessions.length) { + return 0; + } + + const data = {}; + for (const session of sessions) { + const roomId = session.sessionData.room_id; + if (data[roomId] === undefined) { + data[roomId] = {sessions: {}}; + } + + const sessionData = await this._olmDevice.exportInboundGroupSession( + session.senderKey, session.sessionId, session.sessionData, + ); + sessionData.algorithm = olmlib.MEGOLM_ALGORITHM; + delete sessionData.session_id; + delete sessionData.room_id; + const firstKnownIndex = sessionData.first_known_index; + delete sessionData.first_known_index; + const encrypted = this.backupKey.encrypt(JSON.stringify(sessionData)); + + const forwardedCount = + (sessionData.forwarding_curve25519_key_chain || []).length; + + const device = this._deviceList.getDeviceByIdentityKey( + olmlib.MEGOLM_ALGORITHM, session.senderKey, + ); + + data[roomId]['sessions'][session.sessionId] = { + first_message_index: firstKnownIndex, + forwarded_count: forwardedCount, + is_verified: !!(device && device.isVerified()), + session_data: encrypted, + }; + } + + await this._baseApis.sendKeyBackup( + undefined, undefined, this.backupInfo.version, + {rooms: data}, + ); + await this._cryptoStore.unmarkSessionsNeedingBackup(sessions); + + return sessions.length; +}; + Crypto.prototype.backupGroupSession = async function( roomId, senderKey, forwardingCurve25519KeyChain, sessionId, sessionKey, keysClaimed, @@ -1090,7 +1105,9 @@ Crypto.prototype.backupGroupSession = async function( sessionId: sessionId, }]); - await this._maybeSendKeyBackup(); + // don't wait for this to complete: it will delay so + // happens in the background + this._scheduleKeyBackupSend(); }; Crypto.prototype.backupAllGroupSessions = async function(version) { @@ -1109,7 +1126,10 @@ Crypto.prototype.backupAllGroupSessions = async function(version) { }, ); - await this._maybeSendKeyBackup(0, false); + let numKeysBackedUp; + do { + numKeysBackedUp = this._backupPendingKeys(KEY_BACKUP_KEYS_PER_REQUEST); + } while (numKeysBackedUp > 0); }; /* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307 From c5f6f87a6cd603394df684b9b823a79c25307466 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 13 Dec 2018 12:04:43 +0000 Subject: [PATCH 10/17] Add await to ensure we wait for backup errors Signed-off-by: J. Ryan Stinnett --- src/crypto/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/index.js b/src/crypto/index.js index a530f193e..4cd218ee2 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -1128,7 +1128,7 @@ Crypto.prototype.backupAllGroupSessions = async function(version) { let numKeysBackedUp; do { - numKeysBackedUp = this._backupPendingKeys(KEY_BACKUP_KEYS_PER_REQUEST); + numKeysBackedUp = await this._backupPendingKeys(KEY_BACKUP_KEYS_PER_REQUEST); } while (numKeysBackedUp > 0); }; From 2b734b8e69a3acfa7b3e757ca379c554461dfcb1 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 13 Dec 2018 15:53:29 +0000 Subject: [PATCH 11/17] Emit for key backup failures Signed-off-by: J. Ryan Stinnett --- src/client.js | 1 + src/crypto/index.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/client.js b/src/client.js index c942acb9d..339333319 100644 --- a/src/client.js +++ b/src/client.js @@ -447,6 +447,7 @@ MatrixClient.prototype.initCrypto = async function() { ); this.reEmitter.reEmit(crypto, [ + "crypto.keyBackupFailed", "crypto.roomKeyRequest", "crypto.roomKeyRequestCancellation", "crypto.warning", diff --git a/src/crypto/index.js b/src/crypto/index.js index 4cd218ee2..9e8daf93e 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -1023,6 +1023,7 @@ Crypto.prototype._scheduleKeyBackupSend = async function() { ) { // Backup version has changed or this backup version // has been deleted + this.emit("crypto.keyBackupFailed", err.data.errcode); throw err; } } From c92e510a4dd29f71dd05f918fac48f58df9f3c92 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 18 Dec 2018 18:36:24 +0000 Subject: [PATCH 12/17] Pin to base-x 3.0.4 This avoids API changes in 3.0.5 (requiring `Buffer` instances), but more importantly also avoids dealing with ES6 in dependencies for another day. Signed-off-by: J. Ryan Stinnett --- package-lock.json | 2 +- package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index df95672aa..0587c9915 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "0.13.1", + "version": "0.14.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 03fdc40ff..916c1d861 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "dependencies": { "another-json": "^0.2.0", "babel-runtime": "^6.26.0", + "base-x": "3.0.4", "bluebird": "^3.5.0", "browser-request": "^0.3.3", "bs58": "^4.0.1", From a07f0631b7c11e21d1d52da4ee5d683cc4be1b10 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 19 Dec 2018 22:28:40 +0000 Subject: [PATCH 13/17] Include unhomoglyph in package-lock.json - it must have felt left out :L Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package-lock.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package-lock.json b/package-lock.json index 0587c9915..e950a7ff1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6735,6 +6735,11 @@ } } }, + "unhomoglyph": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.2.tgz", + "integrity": "sha1-1p5fWmocayEZQaCIm4HrqGWVwlM=" + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", From e628ed3ef456e0708537324f0d31a4806f466873 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Dec 2018 19:12:01 +0000 Subject: [PATCH 14/17] Add a getAllEndToEndSessions to crypto store So we can migrate them from place to place --- .../store/indexeddb-crypto-store-backend.js | 18 ++++++++++++++++++ src/crypto/store/indexeddb-crypto-store.js | 11 +++++++++++ src/crypto/store/localStorage-crypto-store.js | 11 +++++++++++ src/crypto/store/memory-crypto-store.js | 8 ++++++++ 4 files changed, 48 insertions(+) diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index 566ef9384..5378efd78 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -384,6 +384,24 @@ export class Backend { }; } + getAllEndToEndSessions(txn, func) { + const objectStore = txn.objectStore("sessions"); + const getReq = objectStore.openCursor(); + getReq.onsuccess = function() { + const cursor = getReq.result; + if (cursor) { + func(cursor.value); + cursor.continue(); + } else { + try { + func(null); + } catch (e) { + abortWithException(txn, e); + } + } + }; + } + storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) { const objectStore = txn.objectStore("sessions"); objectStore.put({ diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index bf380b6da..fecc16789 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -336,6 +336,17 @@ export default class IndexedDBCryptoStore { this._backendPromise.value().getEndToEndSessions(deviceKey, txn, func); } + /** + * Retrieve all end-to-end sessions + * @param {*} txn An active transaction. See doTxn(). + * @param {function(object)} func Called one for each session with + * an object with, deviceKey, lastReceivedMessageTs, sessionId + * and session keys. + */ + getAllEndToEndSessions(txn, func) { + this._backendPromise.value().getAllEndToEndSessions(txn, func); + } + /** * Store a session between the logged-in user and another device * @param {string} deviceKey The public key of the other device. diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js index 5ca48a16c..cdb9c251c 100644 --- a/src/crypto/store/localStorage-crypto-store.js +++ b/src/crypto/store/localStorage-crypto-store.js @@ -94,6 +94,17 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { func(this._getEndToEndSessions(deviceKey) || {}); } + getAllEndToEndSessions(txn, func) { + for (let i = 0; i < this.store.length; ++i) { + if (this.store.key(i).startsWith(keyEndToEndSessions(''))) { + const deviceKey = this.store.key(i).split('/')[1]; + for (const sess of Object.values(this._getEndToEndSessions(deviceKey)) { + func(sess); + } + } + } + } + storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) { const sessions = this._getEndToEndSessions(deviceKey) || {}; sessions[sessionId] = sessionInfo; diff --git a/src/crypto/store/memory-crypto-store.js b/src/crypto/store/memory-crypto-store.js index 03753e0e1..0b6e0b6fe 100644 --- a/src/crypto/store/memory-crypto-store.js +++ b/src/crypto/store/memory-crypto-store.js @@ -249,6 +249,14 @@ export default class MemoryCryptoStore { func(this._sessions[deviceKey] || {}); } + getAllEndToEndSessions(txn, func) { + for (const deviceSessions of Object.values(this._sessions)) { + for (const sess of Object.values(deviceSessions)) { + func(sess); + } + } + } + storeEndToEndSession(deviceKey, sessionId, sessionInfo, txn) { let deviceSessions = this._sessions[deviceKey]; if (deviceSessions === undefined) { From 59ae6e3dc01d8da9a8d89634481442d99bb96dd4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 21 Dec 2018 19:21:40 +0000 Subject: [PATCH 15/17] Missing bracket --- src/crypto/store/localStorage-crypto-store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js index cdb9c251c..45c4baff0 100644 --- a/src/crypto/store/localStorage-crypto-store.js +++ b/src/crypto/store/localStorage-crypto-store.js @@ -98,7 +98,7 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { for (let i = 0; i < this.store.length; ++i) { if (this.store.key(i).startsWith(keyEndToEndSessions(''))) { const deviceKey = this.store.key(i).split('/')[1]; - for (const sess of Object.values(this._getEndToEndSessions(deviceKey)) { + for (const sess of Object.values(this._getEndToEndSessions(deviceKey))) { func(sess); } } From 79e155acfb03dac0c5cb2f3416414ae8b6f345ae Mon Sep 17 00:00:00 2001 From: Stephen Solka Date: Fri, 28 Dec 2018 21:33:38 -0500 Subject: [PATCH 16/17] Note that rebase lets you mass signoff commits Signed-off-by: Stephen Solka --- CONTRIBUTING.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 3100fb1d1..436136430 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -113,3 +113,8 @@ include the line in your commit or pull request comment:: can't be accepted. Git makes this trivial - just use the -s flag when you do ``git commit``, having first set ``user.name`` and ``user.email`` git configs (which you should have done anyway :) + +If you forgot to sign off your commits before making your pull request and are on git 2.17+ +you can mass signoff using rebase:: + + git rebase --signoff origin/develop From fc5f3c2fcc1ca8686ba2d42fada5e73a24cb48dc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 30 Dec 2018 00:20:33 +0000 Subject: [PATCH 17/17] re-add empty check after removing hidden chars --- src/models/room-member.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/models/room-member.js b/src/models/room-member.js index ebc8df378..bbc7b5e23 100644 --- a/src/models/room-member.js +++ b/src/models/room-member.js @@ -298,6 +298,12 @@ function calculateDisplayName(selfUserId, displayName, roomState) { return selfUserId; } + // First check if the displayname is something we consider truthy + // after stripping it of zero width characters and padding spaces + if (!utils.removeHiddenChars(displayName)) { + return selfUserId; + } + if (!roomState) { return displayName; }