matrix_sdk_indexeddb/crypto_store/migrations/
v0_to_v5.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Schema-only migrations adding various stores and indices, notably
16//! the first version of `inbound_group_sessions`.
17
18use indexed_db_futures::{
19    database::Database,
20    error::{Error, OpenDbError},
21    Build,
22};
23
24use crate::crypto_store::{
25    keys,
26    migrations::{add_nonunique_index, add_unique_index, do_schema_upgrade, old_keys},
27    Result,
28};
29
30/// Perform schema migrations as needed, up to schema version 5.
31pub(crate) async fn schema_add(name: &str) -> Result<(), OpenDbError> {
32    do_schema_upgrade(name, 5, |tx, old_version| {
33        let db = tx.db();
34        // An old_version of 1 could either mean actually the first version of the
35        // schema, or a completely empty schema that has been created with a
36        // call to `Database::open` with no explicit "version". So, to determine
37        // if we need to create the V1 stores, we actually check if the schema is empty.
38        if db.object_store_names().next().is_none() {
39            schema_add_v1(db)?;
40        }
41
42        if old_version < 2 {
43            schema_add_v2(db)?;
44        }
45
46        if old_version < 3 {
47            schema_add_v3(db)?;
48        }
49
50        if old_version < 4 {
51            schema_add_v4(db)?;
52        }
53
54        if old_version < 5 {
55            schema_add_v5(db)?;
56        }
57
58        Ok(())
59    })
60    .await
61}
62
63fn schema_add_v1(db: &Database) -> Result<(), Error> {
64    db.create_object_store(keys::CORE).build()?;
65    db.create_object_store(keys::SESSION).build()?;
66
67    db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1).build()?;
68    db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS).build()?;
69    db.create_object_store(keys::TRACKED_USERS).build()?;
70    db.create_object_store(keys::OLM_HASHES).build()?;
71    db.create_object_store(keys::DEVICES).build()?;
72
73    db.create_object_store(keys::IDENTITIES).build()?;
74    db.create_object_store(keys::BACKUP_KEYS).build()?;
75
76    Ok(())
77}
78
79fn schema_add_v2(db: &Database) -> Result<(), Error> {
80    // We changed how we store inbound group sessions, the key used to
81    // be a tuple of `(room_id, sender_key, session_id)` now it's a
82    // tuple of `(room_id, session_id)`
83    //
84    // Let's just drop the whole object store.
85    db.delete_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1)?;
86    db.create_object_store(old_keys::INBOUND_GROUP_SESSIONS_V1).build()?;
87
88    db.create_object_store(keys::ROOM_SETTINGS).build()?;
89
90    Ok(())
91}
92
93fn schema_add_v3(db: &Database) -> Result<(), Error> {
94    // We changed the way we store outbound session.
95    // ShareInfo changed from a struct to an enum with struct variant.
96    // Let's just discard the existing outbounds
97    db.delete_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
98    db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS).build()?;
99
100    // Support for MSC2399 withheld codes
101    db.create_object_store(old_keys::DIRECT_WITHHELD_INFO).build()?;
102
103    Ok(())
104}
105
106fn schema_add_v4(db: &Database) -> Result<(), Error> {
107    db.create_object_store(keys::SECRETS_INBOX).build()?;
108    Ok(())
109}
110
111fn schema_add_v5(db: &Database) -> Result<(), Error> {
112    // Create a new store for outgoing secret requests
113    let object_store = db.create_object_store(keys::GOSSIP_REQUESTS).build()?;
114
115    add_nonunique_index(&object_store, keys::GOSSIP_REQUESTS_UNSENT_INDEX, "unsent")?;
116
117    add_unique_index(&object_store, keys::GOSSIP_REQUESTS_BY_INFO_INDEX, "info")?;
118
119    if db.object_store_names().any(|n| n == "outgoing_secret_requests") {
120        // Delete the old store names. We just delete any existing requests.
121        db.delete_object_store("outgoing_secret_requests")?;
122        db.delete_object_store("unsent_secret_requests")?;
123        db.delete_object_store("secret_requests_by_info")?;
124    }
125
126    Ok(())
127}