add, don't replace timeline retry entries

also, filter out any that have been decrypted already
This commit is contained in:
Bruno Windels 2021-03-03 11:27:55 +01:00
parent 43547e0901
commit f3c49e51f2
2 changed files with 48 additions and 36 deletions

View file

@ -390,14 +390,16 @@ export class RoomEncryption {
await hsApi.sendToDevice(type, payload, txnId, {log}).response(); await hsApi.sendToDevice(type, payload, txnId, {log}).response();
} }
filterEventEntriesForKeys(entries, keys) { filterUndecryptedEventEntriesForKeys(entries, keys) {
return entries.filter(entry => { return entries.filter(entry => {
if (entry.isEncrypted && !entry.isDecrypted) {
const {event} = entry; const {event} = entry;
if (event) { if (event) {
const senderKey = event.content?.["sender_key"]; const senderKey = event.content?.["sender_key"];
const sessionId = event.content?.["session_id"]; const sessionId = event.content?.["session_id"];
return keys.some(key => senderKey === key.senderKey && sessionId === key.sessionId); return keys.some(key => senderKey === key.senderKey && sessionId === key.sessionId);
} }
}
return false; return false;
}); });
} }

View file

@ -156,6 +156,42 @@ export class Room extends EventEmitter {
return request; return request;
} }
async _prepareSyncDecryption(events, newKeys, roomEncryption, txn, log) {
let retryEntries;
let decryptPreparation;
// when new keys arrive, also see if any events that can now be retried to decrypt
if (newKeys) {
const entriesPerKey = await Promise.all(newKeys.map(key => this._getRetryDecryptEntriesForKey(key, txn)));
retryEntries = entriesPerKey.reduce((allEntries, entries) => allEntries.concat(entries), []);
// If we have the timeline open, see if there are more entries for the new keys
// as we only store missing session information for synced events, not backfilled.
// We want to decrypt all events we can though if the user is looking
// at them when the timeline is open
if (this._timeline) {
let retryTimelineEntries = this._roomEncryption.filterUndecryptedEventEntriesForKeys(this._timeline.remoteEntries, newKeys);
// filter out any entries already in retryEntries so we don't decrypt them twice
const existingIds = retryEntries.reduce((ids, e) => {ids.add(e.id); return ids;}, new Set());
retryTimelineEntries = retryTimelineEntries.filter(e => !existingIds.has(e.id));
// make copies so we don't modify the original entry in writeSync, before the afterSync stage
const retryTimelineEntriesCopies = retryTimelineEntries.map(e => e.clone());
// add to other retry entries
retryEntries = retryEntries.concat(retryTimelineEntriesCopies);
}
if (retryEntries.length) {
log.set("retry", retryEntries.length);
events = events.concat(retryEntries.map(entry => entry.event));
}
}
const eventsToDecrypt = events.filter(event => {
return event?.type === EVENT_ENCRYPTED_TYPE;
});
if (eventsToDecrypt.length) {
decryptPreparation = await roomEncryption.prepareDecryptAll(
eventsToDecrypt, newKeys, DecryptionSource.Sync, txn);
}
return {retryEntries, decryptPreparation};
}
async prepareSync(roomResponse, membership, newKeys, txn, log) { async prepareSync(roomResponse, membership, newKeys, txn, log) {
log.set("id", this.id); log.set("id", this.id);
if (newKeys) { if (newKeys) {
@ -172,36 +208,10 @@ export class Room extends EventEmitter {
let retryEntries; let retryEntries;
let decryptPreparation; let decryptPreparation;
if (roomEncryption) { if (roomEncryption) {
// also look for events in timeline here const events = roomResponse?.timeline?.events || [];
let events = roomResponse?.timeline?.events || []; const result = await this._prepareSyncDecryption(events, newKeys, roomEncryption, txn, log);
// when new keys arrive, also see if any events that can now be retried to decrypt retryEntries = result.retryEntries;
if (newKeys) { decryptPreparation = result.decryptPreparation;
const entriesPerKey = await Promise.all(newKeys.map(key => this._getRetryDecryptEntriesForKey(key, txn)));
retryEntries = entriesPerKey.reduce((allEntries, entries) => allEntries.concat(entries), []);
// If we have the timeline open, see if there are more entries for the new keys
// as we only store missing session for synced and not backfilled events.
// We want to decrypt all events we can though if the user is looking
// at them given the timeline is open
if (this._timeline) {
let retryTimelineEntries = this._roomEncryption.filterEventEntriesForKeys(this._timeline.remoteEntries, newKeys);
// filter out any entries already in retryEntries so we don't decrypt them twice
const existingIds = new Set(retryEntries.map(e => e.id));
retryTimelineEntries = retryTimelineEntries.filter(e => !existingIds.has(e.id))
// make copies so we don't modify the original entry before the afterSync stage
retryEntries = retryTimelineEntries.map(e => e.clone());
}
if (retryEntries.length) {
log.set("retry", retryEntries.length);
events = events.concat(retryEntries.map(entry => entry.event));
}
}
const eventsToDecrypt = events.filter(event => {
return event?.type === EVENT_ENCRYPTED_TYPE;
});
if (eventsToDecrypt.length) {
decryptPreparation = await roomEncryption.prepareDecryptAll(
eventsToDecrypt, newKeys, DecryptionSource.Sync, txn);
}
} }
return { return {