diff --git a/src/Cache.cpp b/src/Cache.cpp index 8cee3453..cff0029e 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -111,6 +111,24 @@ Cache::Cache(const QString &userId, QObject *parent) , localUserId_{userId} { setup(); + connect(this, + &Cache::updateUserCacheFlag, + this, + [this](const std::string &user_id) { + std::optional cache_ = getUserCache(user_id); + if (cache_.has_value()) { + cache_.value().isUpdated = false; + setUserCache(user_id, cache_.value()); + } else { + setUserCache(user_id, UserCache{}); + } + }, + Qt::QueuedConnection); + connect(this, + &Cache::deleteLeftUsers, + this, + [this](const std::string &user_id) { deleteUserCache(user_id); }, + Qt::QueuedConnection); } void @@ -1011,7 +1029,7 @@ Cache::saveState(const mtx::responses::Sync &res) savePresence(txn, res.presence); - // updateUserCache(res.device_lists); + updateUserCache(res.device_lists); removeLeftRooms(txn, res.rooms.leave); @@ -2889,13 +2907,15 @@ Cache::statusMessage(const std::string &user_id) void to_json(json &j, const UserCache &info) { - j["keys"] = info.keys; + j["keys"] = info.keys; + j["isUpdated"] = info.isUpdated; } void from_json(const json &j, UserCache &info) { - info.keys = j.at("keys").get(); + info.keys = j.at("keys").get(); + info.isUpdated = j.at("isUpdated").get(); } std::optional @@ -2935,26 +2955,12 @@ Cache::setUserCache(const std::string &user_id, const UserCache &body) void Cache::updateUserCache(const mtx::responses::DeviceLists body) { - for (auto user_id : body.changed) { - mtx::requests::QueryKeys req; - req.device_keys[user_id] = {}; - - http::client()->query_keys( - req, - [user_id, this](const mtx::responses::QueryKeys res, mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } - - setUserCache(user_id, UserCache{std::move(res)}); - }); + for (std::string user_id : body.changed) { + emit updateUserCacheFlag(user_id); } for (std::string user_id : body.left) { - deleteUserCache(user_id); + emit deleteLeftUsers(user_id); } } diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h index ba746f59..1dde21ce 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h @@ -71,9 +71,12 @@ struct UserCache { //! map of public key key_ids and their public_key mtx::responses::QueryKeys keys; + //! if the current cache is updated or not + bool isUpdated = false; - UserCache(mtx::responses::QueryKeys res) + UserCache(mtx::responses::QueryKeys res, bool isUpdated_ = false) : keys(res) + , isUpdated(isUpdated_) {} UserCache() {} }; diff --git a/src/Cache_p.h b/src/Cache_p.h index f75b0f4e..174090a9 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -269,6 +269,8 @@ signals: void newReadReceipts(const QString &room_id, const std::vector &event_ids); void roomReadStatus(const std::map &status); void removeNotification(const QString &room_id, const QString &event_id); + void updateUserCacheFlag(const std::string &user_id); + void deleteLeftUsers(const std::string &user_id); private: //! Save an invited room. diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index b97b6b30..f8cb31a2 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -52,6 +52,8 @@ #include "blurhash.hpp" +#include // only for debugging + // TODO: Needs to be updated with an actual secret. static const std::string STORAGE_SECRET_KEY("secret"); @@ -1446,3 +1448,36 @@ ChatPage::initiateLogout() emit showOverlayProgressBar(); } + +void +ChatPage::query_keys( + const mtx::requests::QueryKeys &req, + std::function cb) +{ + std::string user_id = req.device_keys.begin()->first; + auto cache_ = cache::getUserCache(user_id); + + if (cache_.has_value()) { + if (cache_.value().isUpdated) { + cb(cache_.value().keys, {}); + } else { + http::client()->query_keys( + req, + [cb, user_id](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + std::cout << "Over here " << user_id << std::endl; + cache::setUserCache(std::move(user_id), + std::move(UserCache{res, true})); + cb(res, err); + }); + } + } else { + http::client()->query_keys(req, cb); + } +} diff --git a/src/ChatPage.h b/src/ChatPage.h index 72adfe19..10801342 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -89,6 +89,9 @@ public: //! Show the room/group list (if it was visible). void showSideBars(); void initiateLogout(); + void query_keys( + const mtx::requests::QueryKeys &req, + std::function cb); void focusMessageInput(); QString status() const; diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 87eae001..2426fe6c 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -84,80 +84,125 @@ UserProfile::getUserStatus() return isUserVerified; } -void -UserProfile::callback_fn(const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err, - std::string user_id) -{ - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } - - if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); - return; - } - - auto devices = res.device_keys.at(user_id); - std::vector deviceInfo; - auto device_verified = cache::getVerifiedCache(user_id); - - for (const auto &d : devices) { - auto device = d.second; - - // TODO: Verify signatures and ignore those that don't pass. - verification::Status verified = verification::Status::UNVERIFIED; - isUserVerified = device_verified->is_user_verified; - if (device_verified.has_value()) { - if (std::find(device_verified->cross_verified.begin(), - device_verified->cross_verified.end(), - d.first) != device_verified->cross_verified.end()) - verified = verification::Status::VERIFIED; - if (std::find(device_verified->device_verified.begin(), - device_verified->device_verified.end(), - d.first) != device_verified->device_verified.end()) - verified = verification::Status::VERIFIED; - if (std::find(device_verified->device_blocked.begin(), - device_verified->device_blocked.end(), - d.first) != device_verified->device_blocked.end()) - verified = verification::Status::BLOCKED; - } - - deviceInfo.push_back( - {QString::fromStdString(d.first), - QString::fromStdString(device.unsigned_info.device_display_name), - verified}); - } - - std::sort( - deviceInfo.begin(), deviceInfo.end(), [](const DeviceInfo &a, const DeviceInfo &b) { - return a.device_id > b.device_id; - }); - - this->deviceList_.queueReset(std::move(deviceInfo)); -} - void UserProfile::fetchDeviceList(const QString &userID) { - auto localUser = utils::localUser(); - auto user_cache = cache::getUserCache(userID.toStdString()); + auto localUser = utils::localUser(); - if (user_cache.has_value()) { - this->callback_fn(user_cache->keys, {}, userID.toStdString()); - } else { - mtx::requests::QueryKeys req; - req.device_keys[userID.toStdString()] = {}; - http::client()->query_keys( - req, - [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - this->callback_fn(res, err, user_id); - }); - } + mtx::requests::QueryKeys req; + req.device_keys[userID.toStdString()] = {}; + ChatPage::instance()->query_keys( + req, + [user_id = userID.toStdString(), local_user_id = localUser.toStdString(), this]( + const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + if (res.device_keys.empty() || + (res.device_keys.find(user_id) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } + + auto devices = res.device_keys.at(user_id); + std::vector deviceInfo; + auto device_verified = cache::getVerifiedCache(user_id); + + for (const auto &d : devices) { + auto device = d.second; + + // TODO: Verify signatures and ignore those that don't pass. + verification::Status verified = verification::Status::UNVERIFIED; + isUserVerified = device_verified->is_user_verified; + if (device_verified.has_value()) { + if (std::find(device_verified->cross_verified.begin(), + device_verified->cross_verified.end(), + d.first) != device_verified->cross_verified.end()) + verified = verification::Status::VERIFIED; + if (std::find(device_verified->device_verified.begin(), + device_verified->device_verified.end(), + d.first) != device_verified->device_verified.end()) + verified = verification::Status::VERIFIED; + if (std::find(device_verified->device_blocked.begin(), + device_verified->device_blocked.end(), + d.first) != device_verified->device_blocked.end()) + verified = verification::Status::BLOCKED; + } + + deviceInfo.push_back( + {QString::fromStdString(d.first), + QString::fromStdString(device.unsigned_info.device_display_name), + verified}); + } + + // Finding if the User is Verified or not based on the Signatures + mtx::requests::QueryKeys req; + req.device_keys[local_user_id] = {}; + + ChatPage::instance()->query_keys( + req, + [&local_user_id, &user_id, other_res = res, this]( + const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { + using namespace mtx; + + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + std::optional lmk, lsk, luk, mk, sk, uk; + + if (res.master_keys.find(local_user_id) != res.master_keys.end()) + lmk = res.master_keys.at(local_user_id); + if (res.user_signing_keys.find(local_user_id) != + res.user_signing_keys.end()) + luk = res.user_signing_keys.at(local_user_id); + if (res.self_signing_keys.find(local_user_id) != + res.self_signing_keys.end()) + lsk = res.self_signing_keys.at(local_user_id); + if (other_res.master_keys.find(user_id) != other_res.master_keys.end()) + mk = other_res.master_keys.at(user_id); + if (other_res.user_signing_keys.find(user_id) != + other_res.user_signing_keys.end()) + uk = other_res.user_signing_keys.at(user_id); + if (other_res.self_signing_keys.find(user_id) != + other_res.self_signing_keys.end()) + sk = other_res.self_signing_keys.at(user_id); + + // First checking if the user is verified + if (lmk.has_value() && luk.has_value()) { + bool is_user_verified = false; + for (auto sign_key : lmk.value().keys) { + if (!luk.value().signatures.empty()) { + for (auto signature : + luk.value().signatures.at(local_user_id)) { + is_user_verified = + is_user_verified || + (olm::client()->ed25519_verify_sig( + sign_key.second, + json(luk.value()), + signature.second)); + } + } + } + std::cout << (isUserVerified ? "Yes" : "No") << std::endl; + } + }); + + std::sort(deviceInfo.begin(), + deviceInfo.end(), + [](const DeviceInfo &a, const DeviceInfo &b) { + return a.device_id > b.device_id; + }); + + this->deviceList_.queueReset(std::move(deviceInfo)); + }); } void