summary refs log tree commit diff
path: root/src/Cache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Cache.cpp')
-rw-r--r--src/Cache.cpp340
1 files changed, 175 insertions, 165 deletions
diff --git a/src/Cache.cpp b/src/Cache.cpp

index b37f69b3..97e99700 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp
@@ -50,7 +50,7 @@ static lmdb::val CACHE_FORMAT_VERSION_KEY("cache_format_version"); constexpr size_t MAX_RESTORED_MESSAGES = 30'000; constexpr auto DB_SIZE = 32ULL * 1024ULL * 1024ULL * 1024ULL; // 32 GB -constexpr auto MAX_DBS = 8092UL; +constexpr auto MAX_DBS = 32384UL; constexpr auto BATCH_SIZE = 100; //! Cache databases and their format. @@ -85,8 +85,6 @@ constexpr auto OUTBOUND_MEGOLM_SESSIONS_DB("outbound_megolm_sessions"); using CachedReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>; using Receipts = std::map<std::string, std::map<std::string, uint64_t>>; -Q_DECLARE_METATYPE(SearchResult) -Q_DECLARE_METATYPE(std::vector<SearchResult>) Q_DECLARE_METATYPE(RoomMember) Q_DECLARE_METATYPE(mtx::responses::Timeline) Q_DECLARE_METATYPE(RoomSearchResult) @@ -320,52 +318,67 @@ Cache::saveInboundMegolmSession(const MegolmSessionIndex &index, auto txn = lmdb::txn::begin(env_); lmdb::dbi_put(txn, inboundMegolmSessionDb_, lmdb::val(key), lmdb::val(pickled)); txn.commit(); - - { - std::unique_lock<std::mutex> lock(session_storage.group_inbound_mtx); - session_storage.group_inbound_sessions[key] = std::move(session); - } } -OlmInboundGroupSession * +mtx::crypto::InboundGroupSessionPtr Cache::getInboundMegolmSession(const MegolmSessionIndex &index) { - std::unique_lock<std::mutex> lock(session_storage.group_inbound_mtx); - return session_storage.group_inbound_sessions[json(index).dump()].get(); + using namespace mtx::crypto; + + try { + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + std::string key = json(index).dump(); + lmdb::val value; + + if (lmdb::dbi_get(txn, inboundMegolmSessionDb_, lmdb::val(key), value)) { + auto session = unpickle<InboundSessionObject>( + std::string(value.data(), value.size()), SECRET); + return session; + } + } catch (std::exception &e) { + nhlog::db()->error("Failed to get inbound megolm session {}", e.what()); + } + + return nullptr; } bool Cache::inboundMegolmSessionExists(const MegolmSessionIndex &index) { - std::unique_lock<std::mutex> lock(session_storage.group_inbound_mtx); - return session_storage.group_inbound_sessions.find(json(index).dump()) != - session_storage.group_inbound_sessions.end(); + using namespace mtx::crypto; + + try { + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + std::string key = json(index).dump(); + lmdb::val value; + + return lmdb::dbi_get(txn, inboundMegolmSessionDb_, lmdb::val(key), value); + } catch (std::exception &e) { + nhlog::db()->error("Failed to get inbound megolm session {}", e.what()); + } + + return false; } void -Cache::updateOutboundMegolmSession(const std::string &room_id, int message_index) +Cache::updateOutboundMegolmSession(const std::string &room_id, + const OutboundGroupSessionData &data_, + mtx::crypto::OutboundGroupSessionPtr &ptr) { using namespace mtx::crypto; if (!outboundMegolmSessionExists(room_id)) return; - OutboundGroupSessionData data; - OlmOutboundGroupSession *session; - { - std::unique_lock<std::mutex> lock(session_storage.group_outbound_mtx); - data = session_storage.group_outbound_session_data[room_id]; - session = session_storage.group_outbound_sessions[room_id].get(); - - // Update with the current message. - data.message_index = message_index; - session_storage.group_outbound_session_data[room_id] = data; - } + OutboundGroupSessionData data = data_; + data.message_index = olm_outbound_group_session_message_index(ptr.get()); + data.session_id = mtx::crypto::session_id(ptr.get()); + data.session_key = mtx::crypto::session_key(ptr.get()); // Save the updated pickled data for the session. json j; j["data"] = data; - j["session"] = pickle<OutboundSessionObject>(session, SECRET); + j["session"] = pickle<OutboundSessionObject>(ptr.get(), SECRET); auto txn = lmdb::txn::begin(env_); lmdb::dbi_put(txn, outboundMegolmSessionDb_, lmdb::val(room_id), lmdb::val(j.dump())); @@ -381,10 +394,6 @@ Cache::dropOutboundMegolmSession(const std::string &room_id) return; { - std::unique_lock<std::mutex> lock(session_storage.group_outbound_mtx); - session_storage.group_outbound_session_data.erase(room_id); - session_storage.group_outbound_sessions.erase(room_id); - auto txn = lmdb::txn::begin(env_); lmdb::dbi_del(txn, outboundMegolmSessionDb_, lmdb::val(room_id), nullptr); txn.commit(); @@ -394,7 +403,7 @@ Cache::dropOutboundMegolmSession(const std::string &room_id) void Cache::saveOutboundMegolmSession(const std::string &room_id, const OutboundGroupSessionData &data, - mtx::crypto::OutboundGroupSessionPtr session) + mtx::crypto::OutboundGroupSessionPtr &session) { using namespace mtx::crypto; const auto pickled = pickle<OutboundSessionObject>(session.get(), SECRET); @@ -406,30 +415,40 @@ Cache::saveOutboundMegolmSession(const std::string &room_id, auto txn = lmdb::txn::begin(env_); lmdb::dbi_put(txn, outboundMegolmSessionDb_, lmdb::val(room_id), lmdb::val(j.dump())); txn.commit(); - - { - std::unique_lock<std::mutex> lock(session_storage.group_outbound_mtx); - session_storage.group_outbound_session_data[room_id] = data; - session_storage.group_outbound_sessions[room_id] = std::move(session); - } } bool Cache::outboundMegolmSessionExists(const std::string &room_id) noexcept { - std::unique_lock<std::mutex> lock(session_storage.group_outbound_mtx); - return (session_storage.group_outbound_sessions.find(room_id) != - session_storage.group_outbound_sessions.end()) && - (session_storage.group_outbound_session_data.find(room_id) != - session_storage.group_outbound_session_data.end()); + try { + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + lmdb::val value; + return lmdb::dbi_get(txn, outboundMegolmSessionDb_, lmdb::val(room_id), value); + } catch (std::exception &e) { + nhlog::db()->error("Failed to retrieve outbound Megolm Session: {}", e.what()); + return false; + } } OutboundGroupSessionDataRef Cache::getOutboundMegolmSession(const std::string &room_id) { - std::unique_lock<std::mutex> lock(session_storage.group_outbound_mtx); - return OutboundGroupSessionDataRef{session_storage.group_outbound_sessions[room_id].get(), - session_storage.group_outbound_session_data[room_id]}; + try { + using namespace mtx::crypto; + + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + lmdb::val value; + lmdb::dbi_get(txn, outboundMegolmSessionDb_, lmdb::val(room_id), value); + auto obj = json::parse(std::string_view(value.data(), value.size())); + + OutboundGroupSessionDataRef ref{}; + ref.data = obj.at("data").get<OutboundGroupSessionData>(); + ref.session = unpickle<OutboundSessionObject>(obj.at("session"), SECRET); + return ref; + } catch (std::exception &e) { + nhlog::db()->error("Failed to retrieve outbound Megolm Session: {}", e.what()); + return {}; + } } // @@ -539,56 +558,6 @@ Cache::saveOlmAccount(const std::string &data) txn.commit(); } -void -Cache::restoreSessions() -{ - using namespace mtx::crypto; - - auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - std::string key, value; - - // - // Inbound Megolm Sessions - // - { - auto cursor = lmdb::cursor::open(txn, inboundMegolmSessionDb_); - while (cursor.get(key, value, MDB_NEXT)) { - auto session = unpickle<InboundSessionObject>(value, SECRET); - session_storage.group_inbound_sessions[key] = std::move(session); - } - cursor.close(); - } - - // - // Outbound Megolm Sessions - // - { - auto cursor = lmdb::cursor::open(txn, outboundMegolmSessionDb_); - while (cursor.get(key, value, MDB_NEXT)) { - json obj; - - try { - obj = json::parse(value); - - session_storage.group_outbound_session_data[key] = - obj.at("data").get<OutboundGroupSessionData>(); - - auto session = - unpickle<OutboundSessionObject>(obj.at("session"), SECRET); - session_storage.group_outbound_sessions[key] = std::move(session); - } catch (const nlohmann::json::exception &e) { - nhlog::db()->critical( - "failed to parse outbound megolm session data: {}", e.what()); - } - } - cursor.close(); - } - - txn.commit(); - - nhlog::db()->info("sessions restored"); -} - std::string Cache::restoreOlmAccount() { @@ -1164,12 +1133,10 @@ Cache::saveState(const mtx::responses::Sync &res) saveTimelineMessages(txn, room.first, room.second.timeline); RoomInfo updatedInfo; - updatedInfo.name = getRoomName(txn, statesdb, membersdb).toStdString(); - updatedInfo.topic = getRoomTopic(txn, statesdb).toStdString(); - updatedInfo.avatar_url = - getRoomAvatarUrl(txn, statesdb, membersdb, QString::fromStdString(room.first)) - .toStdString(); - updatedInfo.version = getRoomVersion(txn, statesdb).toStdString(); + updatedInfo.name = getRoomName(txn, statesdb, membersdb).toStdString(); + updatedInfo.topic = getRoomTopic(txn, statesdb).toStdString(); + updatedInfo.avatar_url = getRoomAvatarUrl(txn, statesdb, membersdb).toStdString(); + updatedInfo.version = getRoomVersion(txn, statesdb).toStdString(); // Process the account_data associated with this room if (!room.second.account_data.events.empty()) { @@ -1812,6 +1779,17 @@ Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) e.what()); return {}; } + auto membersdb{0}; + + try { + membersdb = getMembersDb(txn, room_id); + } catch (lmdb::runtime_error &e) { + nhlog::db()->error("Can't open db for room '{}', probably doesn't exist yet. ({})", + room_id, + e.what()); + return {}; + } + if (orderDb.size(txn) == 0) return DescInfo{}; @@ -1854,9 +1832,16 @@ Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) mtx::events::collections::TimelineEvent te; mtx::events::collections::from_json(obj, te); + lmdb::val info; + MemberInfo m; + if (lmdb::dbi_get( + txn, membersdb, lmdb::val(obj["sender"].get<std::string>()), info)) { + m = json::parse(std::string_view(info.data(), info.size())); + } + cursor.close(); return utils::getMessageDescription( - te.data, local_user, QString::fromStdString(room_id)); + te.data, local_user, QString::fromStdString(m.name)); } cursor.close(); @@ -1883,10 +1868,7 @@ Cache::invites() } QString -Cache::getRoomAvatarUrl(lmdb::txn &txn, - lmdb::dbi &statesdb, - lmdb::dbi &membersdb, - const QString &room_id) +Cache::getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) { using namespace mtx::events; using namespace mtx::events::state; @@ -1914,14 +1896,16 @@ Cache::getRoomAvatarUrl(lmdb::txn &txn, auto cursor = lmdb::cursor::open(txn, membersdb); std::string user_id; std::string member_data; + std::string fallback_url; // Resolve avatar for 1-1 chats. while (cursor.get(user_id, member_data, MDB_NEXT)) { - if (user_id == localUserId_.toStdString()) - continue; - try { MemberInfo m = json::parse(member_data); + if (user_id == localUserId_.toStdString()) { + fallback_url = m.avatar_url; + continue; + } cursor.close(); return QString::fromStdString(m.avatar_url); @@ -1933,7 +1917,7 @@ Cache::getRoomAvatarUrl(lmdb::txn &txn, cursor.close(); // Default case when there is only one member. - return avatarUrl(room_id, localUserId_); + return QString::fromStdString(fallback_url); } QString @@ -2293,7 +2277,8 @@ Cache::getMember(const std::string &room_id, const std::string &user_id) MemberInfo m = json::parse(std::string_view(info.data(), info.size())); return m; } - } catch (...) { + } catch (std::exception &e) { + nhlog::db()->warn("Failed to read member ({}): {}", user_id, e.what()); } return std::nullopt; } @@ -2334,39 +2319,6 @@ Cache::searchRooms(const std::string &query, std::uint8_t max_items) return results; } -std::vector<SearchResult> -Cache::searchUsers(const std::string &room_id, const std::string &query, std::uint8_t max_items) -{ - std::multimap<int, std::pair<std::string, std::string>> items; - - auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - auto cursor = lmdb::cursor::open(txn, getMembersDb(txn, room_id)); - - std::string user_id, user_data; - while (cursor.get(user_id, user_data, MDB_NEXT)) { - const auto display_name = displayName(room_id, user_id); - const int score = utils::levenshtein_distance(query, display_name); - - items.emplace(score, std::make_pair(user_id, display_name)); - } - - auto end = items.begin(); - - if (items.size() >= max_items) - std::advance(end, max_items); - else if (items.size() > 0) - std::advance(end, items.size()); - - std::vector<SearchResult> results; - for (auto it = items.begin(); it != end; it++) { - const auto user = it->second; - results.push_back(SearchResult{QString::fromStdString(user.first), - QString::fromStdString(user.second)}); - } - - return results; -} - std::vector<RoomMember> Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_t len) { @@ -3144,6 +3096,39 @@ Cache::roomMembers(const std::string &room_id) return members; } +std::map<std::string, std::optional<UserKeyCache>> +Cache::getMembersWithKeys(const std::string &room_id) +{ + lmdb::val keys; + + try { + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + std::map<std::string, std::optional<UserKeyCache>> members; + + auto db = getMembersDb(txn, room_id); + auto keysDb = getUserKeysDb(txn); + + std::string user_id, unused; + auto cursor = lmdb::cursor::open(txn, db); + while (cursor.get(user_id, unused, MDB_NEXT)) { + auto res = lmdb::dbi_get(txn, keysDb, lmdb::val(user_id), keys); + + if (res) { + members[user_id] = + json::parse(std::string_view(keys.data(), keys.size())) + .get<UserKeyCache>(); + } else { + members[user_id] = {}; + } + } + cursor.close(); + + return members; + } catch (std::exception &) { + return {}; + } +} + QString Cache::displayName(const QString &room_id, const QString &user_id) { @@ -3284,6 +3269,8 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query updates[user].self_signing_keys = keys; for (auto &[user, update] : updates) { + nhlog::db()->debug("Updated user keys: {}", user); + lmdb::val oldKeys; auto res = lmdb::dbi_get(txn, db, lmdb::val(user), oldKeys); @@ -3346,6 +3333,8 @@ Cache::markUserKeysOutOfDate(lmdb::txn &txn, query.token = sync_token; for (const auto &user : user_ids) { + nhlog::db()->debug("Marking user keys out of date: {}", user); + lmdb::val oldKeys; auto res = lmdb::dbi_get(txn, db, lmdb::val(user), oldKeys); @@ -3700,11 +3689,40 @@ from_json(const json &j, MemberInfo &info) } void +to_json(nlohmann::json &obj, const DeviceAndMasterKeys &msg) +{ + obj["devices"] = msg.devices; + obj["master_keys"] = msg.master_keys; +} + +void +from_json(const nlohmann::json &obj, DeviceAndMasterKeys &msg) +{ + msg.devices = obj.at("devices").get<decltype(msg.devices)>(); + msg.master_keys = obj.at("master_keys").get<decltype(msg.master_keys)>(); +} + +void +to_json(nlohmann::json &obj, const SharedWithUsers &msg) +{ + obj["keys"] = msg.keys; +} + +void +from_json(const nlohmann::json &obj, SharedWithUsers &msg) +{ + msg.keys = obj.at("keys").get<std::map<std::string, DeviceAndMasterKeys>>(); +} + +void to_json(nlohmann::json &obj, const OutboundGroupSessionData &msg) { obj["session_id"] = msg.session_id; obj["session_key"] = msg.session_key; obj["message_index"] = msg.message_index; + + obj["initially"] = msg.initially; + obj["currently"] = msg.currently; } void @@ -3713,6 +3731,9 @@ from_json(const nlohmann::json &obj, OutboundGroupSessionData &msg) msg.session_id = obj.at("session_id"); msg.session_key = obj.at("session_key"); msg.message_index = obj.at("message_index"); + + msg.initially = obj.value("initially", SharedWithUsers{}); + msg.currently = obj.value("currently", SharedWithUsers{}); } void @@ -3762,8 +3783,6 @@ namespace cache { void init(const QString &user_id) { - qRegisterMetaType<SearchResult>(); - qRegisterMetaType<std::vector<SearchResult>>(); qRegisterMetaType<RoomMember>(); qRegisterMetaType<RoomSearchResult>(); qRegisterMetaType<RoomInfo>(); @@ -3878,9 +3897,9 @@ getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb) return instance_->getRoomTopic(txn, statesdb); } QString -getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb, const QString &room_id) +getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) { - return instance_->getRoomAvatarUrl(txn, statesdb, membersdb, room_id); + return instance_->getRoomAvatarUrl(txn, statesdb, membersdb); } QString @@ -4075,11 +4094,6 @@ calculateRoomReadStatus() instance_->calculateRoomReadStatus(); } -std::vector<SearchResult> -searchUsers(const std::string &room_id, const std::string &query, std::uint8_t max_items) -{ - return instance_->searchUsers(room_id, query, max_items); -} std::vector<RoomSearchResult> searchRooms(const std::string &query, std::uint8_t max_items) { @@ -4154,9 +4168,9 @@ isRoomMember(const std::string &user_id, const std::string &room_id) void saveOutboundMegolmSession(const std::string &room_id, const OutboundGroupSessionData &data, - mtx::crypto::OutboundGroupSessionPtr session) + mtx::crypto::OutboundGroupSessionPtr &session) { - instance_->saveOutboundMegolmSession(room_id, data, std::move(session)); + instance_->saveOutboundMegolmSession(room_id, data, session); } OutboundGroupSessionDataRef getOutboundMegolmSession(const std::string &room_id) @@ -4169,9 +4183,11 @@ outboundMegolmSessionExists(const std::string &room_id) noexcept return instance_->outboundMegolmSessionExists(room_id); } void -updateOutboundMegolmSession(const std::string &room_id, int message_index) +updateOutboundMegolmSession(const std::string &room_id, + const OutboundGroupSessionData &data, + mtx::crypto::OutboundGroupSessionPtr &session) { - instance_->updateOutboundMegolmSession(room_id, message_index); + instance_->updateOutboundMegolmSession(room_id, data, session); } void dropOutboundMegolmSession(const std::string &room_id) @@ -4199,7 +4215,7 @@ saveInboundMegolmSession(const MegolmSessionIndex &index, { instance_->saveInboundMegolmSession(index, std::move(session)); } -OlmInboundGroupSession * +mtx::crypto::InboundGroupSessionPtr getInboundMegolmSession(const MegolmSessionIndex &index) { return instance_->getInboundMegolmSession(index); @@ -4246,10 +4262,4 @@ restoreOlmAccount() { return instance_->restoreOlmAccount(); } - -void -restoreSessions() -{ - return instance_->restoreSessions(); -} } // namespace cache