diff --git a/src/matrix/storage/common.ts b/src/matrix/storage/common.ts index 3929c061..23bb0d31 100644 --- a/src/matrix/storage/common.ts +++ b/src/matrix/storage/common.ts @@ -33,7 +33,6 @@ export enum StoreNames { groupSessionDecryptions = "groupSessionDecryptions", operations = "operations", accountData = "accountData", - sessionsNeedingBackup = "sessionsNeedingBackup", } export const STORE_NAMES: Readonly = Object.values(StoreNames); diff --git a/src/matrix/storage/idb/Transaction.ts b/src/matrix/storage/idb/Transaction.ts index 9b765310..80894105 100644 --- a/src/matrix/storage/idb/Transaction.ts +++ b/src/matrix/storage/idb/Transaction.ts @@ -36,7 +36,6 @@ import {OutboundGroupSessionStore} from "./stores/OutboundGroupSessionStore"; import {GroupSessionDecryptionStore} from "./stores/GroupSessionDecryptionStore"; import {OperationStore} from "./stores/OperationStore"; import {AccountDataStore} from "./stores/AccountDataStore"; -import {SessionNeedingBackupStore} from "./stores/SessionNeedingBackupStore"; import type {ILogger, ILogItem} from "../../../logging/types"; export type IDBKey = IDBValidKey | IDBKeyRange; @@ -152,10 +151,6 @@ export class Transaction { get inboundGroupSessions(): InboundGroupSessionStore { return this._store(StoreNames.inboundGroupSessions, idbStore => new InboundGroupSessionStore(idbStore)); } - - get sessionsNeedingBackup(): SessionNeedingBackupStore { - return this._store(StoreNames.sessionsNeedingBackup, idbStore => new SessionNeedingBackupStore(idbStore)); - } get outboundGroupSessions(): OutboundGroupSessionStore { return this._store(StoreNames.outboundGroupSessions, idbStore => new OutboundGroupSessionStore(idbStore)); diff --git a/src/matrix/storage/idb/schema.ts b/src/matrix/storage/idb/schema.ts index 2da4d65c..e6644fd0 100644 --- a/src/matrix/storage/idb/schema.ts +++ b/src/matrix/storage/idb/schema.ts @@ -6,8 +6,7 @@ import {addRoomToIdentity} from "../../e2ee/DeviceTracker.js"; import {SESSION_E2EE_KEY_PREFIX} from "../../e2ee/common.js"; import {SummaryData} from "../../room/RoomSummary"; import {RoomMemberStore, MemberData} from "./stores/RoomMemberStore"; -import {encodeKey as encodeBackupKey} from "./stores/SessionNeedingBackupStore"; -import {InboundGroupSessionStore, InboundGroupSessionEntry} from "./stores/InboundGroupSessionStore"; +import {InboundGroupSessionStore, InboundGroupSessionEntry, BackupStatus} from "./stores/InboundGroupSessionStore"; import {RoomStateEntry} from "./stores/RoomStateStore"; import {SessionStore} from "./stores/SessionStore"; import {Store} from "./Store"; @@ -34,7 +33,7 @@ export const schema: MigrationFunc[] = [ changeSSSSKeyPrefix, backupAndRestoreE2EEAccountToLocalStorage, clearAllStores, - createSessionsNeedingBackup + addInboundSessionBackupIndex ]; // TODO: how to deal with git merge conflicts of this array? @@ -279,26 +278,12 @@ async function clearAllStores(db: IDBDatabase, txn: IDBTransaction) { } } -// v15 adds the sessionsNeedingBackup store, for key backup -async function createSessionsNeedingBackup(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem): Promise { - const backupStore = db.createObjectStore("sessionsNeedingBackup", {keyPath: "key"}); - const session = txn.objectStore("session"); - const ssssKey = await reqAsPromise(session.get(`${SESSION_E2EE_KEY_PREFIX}ssssKey`) as IDBRequest); - const keyBackupEnabled = !!ssssKey; - log.set("key backup", keyBackupEnabled); - if (keyBackupEnabled) { - let count = 0; - try { - const inboundGroupSessions = txn.objectStore("inboundGroupSessions"); - await iterateCursor(inboundGroupSessions.openCursor(), session => { - backupStore.add({key: encodeBackupKey(session.roomId, session.senderKey, session.sessionId)}); - count += 1; - return NOT_DONE; - }); - } catch (err) { - txn.abort(); - log.log("could not migrate operations").catch(err); - } - log.set("count", count); - } +// v15 add backup index to inboundGroupSessions +async function addInboundSessionBackupIndex(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem): Promise { + const inboundGroupSessions = txn.objectStore("inboundGroupSessions"); + await iterateCursor(inboundGroupSessions.openCursor(), (value, key, cursor) => { + value.backup = BackupStatus.NotBackedUp; + return NOT_DONE; + }); + inboundGroupSessions.createIndex("byBackup", "backup", {unique: false}); } diff --git a/src/matrix/storage/idb/stores/InboundGroupSessionStore.ts b/src/matrix/storage/idb/stores/InboundGroupSessionStore.ts index c670302c..72ea6e9a 100644 --- a/src/matrix/storage/idb/stores/InboundGroupSessionStore.ts +++ b/src/matrix/storage/idb/stores/InboundGroupSessionStore.ts @@ -17,6 +17,11 @@ limitations under the License. import {MIN_UNICODE, MAX_UNICODE} from "./common"; import {Store} from "../Store"; +export enum BackupStatus { + NotBackedUp = 0, + BackedUp = 1 +} + export interface InboundGroupSessionEntry { roomId: string; senderKey: string; @@ -24,6 +29,7 @@ export interface InboundGroupSessionEntry { session?: string; claimedKeys?: { [algorithm : string] : string }; eventIds?: string[]; + backup: BackupStatus } type InboundGroupSessionStorageEntry = InboundGroupSessionEntry & { key: string }; @@ -63,4 +69,19 @@ export class InboundGroupSessionStore { ); this._store.delete(range); } + countNonBackedUpSessions(): Promise { + return this._store.index("byBackup").count(); + } + + getFirstNonBackedUpSessions(amount: number): Promise { + return this._store.index("byBackup").selectLimit(0, amount); + } + + async markAsBackedUp(roomId: string, senderKey: string, sessionId: string): Promise { + const entry = await this._store.get(encodeKey(roomId, senderKey, sessionId)); + if (entry) { + entry.backup = BackupStatus.BackedUp; + this._store.put(entry); + } + } } diff --git a/src/matrix/storage/idb/stores/SessionNeedingBackupStore.ts b/src/matrix/storage/idb/stores/SessionNeedingBackupStore.ts deleted file mode 100644 index 327fce9a..00000000 --- a/src/matrix/storage/idb/stores/SessionNeedingBackupStore.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import type {Store} from "../Store"; - -export type BackupEntry = { - roomId: string; - senderKey: string; - sessionId: string; -}; - -type StorageEntry = { - key: string -}; - -export function encodeKey(roomId: string, senderKey: string, sessionId: string): string { - return `${roomId}|${senderKey}|${sessionId}`; -} - -function decodeKey(key: string): BackupEntry { - const [roomId, senderKey, sessionId] = key.split("|"); - return {roomId, senderKey, sessionId}; -} - -export class SessionNeedingBackupStore { - constructor(private store: Store) {} - - async getFirstEntries(amount: number): Promise { - const storageEntries = await this.store.selectLimit(undefined, amount); - return storageEntries.map(s => decodeKey(s.key)); - } - - set(roomId: string, senderKey: string, sessionId: string): void { - const storageEntry : StorageEntry = { - key: encodeKey(roomId, senderKey, sessionId), - }; - this.store.put(storageEntry); - } - - remove(roomId: string, senderKey: string, sessionId: string): void { - this.store.delete(encodeKey(roomId, senderKey, sessionId)); - } - - count(): Promise { - return this.store.count(); - } -}