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
|