summary refs log tree commit diff
path: root/include/Cache.h
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2018-07-17 16:37:25 +0300
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2018-07-17 16:37:25 +0300
commit0e814da91c8e041897a4c3f7e6e9234bbc7c6f7a (patch)
tree21f655d30630fe77ba48d07e4b357e2b6c6a5730 /include/Cache.h
parentMerge pull request #372 from bebehei/notification (diff)
downloadnheko-0e814da91c8e041897a4c3f7e6e9234bbc7c6f7a.tar.xz
Move all files under src/
Diffstat (limited to 'include/Cache.h')
-rw-r--r--include/Cache.h661
1 files changed, 0 insertions, 661 deletions
diff --git a/include/Cache.h b/include/Cache.h
deleted file mode 100644

index 14f991e8..00000000 --- a/include/Cache.h +++ /dev/null
@@ -1,661 +0,0 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#pragma once - -#include <boost/optional.hpp> - -#include <QDateTime> -#include <QDir> -#include <QImage> -#include <QString> - -#include <json.hpp> -#include <lmdb++.h> -#include <mtx/events/join_rules.hpp> -#include <mtx/responses.hpp> -#include <mtxclient/crypto/client.hpp> -#include <mutex> - -#include "Logging.hpp" - -using mtx::events::state::JoinRule; - -struct RoomMember -{ - QString user_id; - QString display_name; - QImage avatar; -}; - -struct SearchResult -{ - QString user_id; - QString display_name; -}; - -static int -numeric_key_comparison(const MDB_val *a, const MDB_val *b) -{ - auto lhs = std::stoull(std::string((char *)a->mv_data, a->mv_size)); - auto rhs = std::stoull(std::string((char *)b->mv_data, b->mv_size)); - - if (lhs < rhs) - return 1; - else if (lhs == rhs) - return 0; - - return -1; -} - -Q_DECLARE_METATYPE(SearchResult) -Q_DECLARE_METATYPE(QVector<SearchResult>) -Q_DECLARE_METATYPE(RoomMember) -Q_DECLARE_METATYPE(mtx::responses::Timeline) - -//! Used to uniquely identify a list of read receipts. -struct ReadReceiptKey -{ - std::string event_id; - std::string room_id; -}; - -inline void -to_json(json &j, const ReadReceiptKey &key) -{ - j = json{{"event_id", key.event_id}, {"room_id", key.room_id}}; -} - -inline void -from_json(const json &j, ReadReceiptKey &key) -{ - key.event_id = j.at("event_id").get<std::string>(); - key.room_id = j.at("room_id").get<std::string>(); -} - -struct DescInfo -{ - QString username; - QString userid; - QString body; - QString timestamp; - QDateTime datetime; -}; - -//! UI info associated with a room. -struct RoomInfo -{ - //! The calculated name of the room. - std::string name; - //! The topic of the room. - std::string topic; - //! The calculated avatar url of the room. - std::string avatar_url; - //! Whether or not the room is an invite. - bool is_invite = false; - //! Total number of members in the room. - int16_t member_count = 0; - //! Who can access to the room. - JoinRule join_rule = JoinRule::Public; - bool guest_access = false; - //! Metadata describing the last message in the timeline. - DescInfo msgInfo; -}; - -inline void -to_json(json &j, const RoomInfo &info) -{ - j["name"] = info.name; - j["topic"] = info.topic; - j["avatar_url"] = info.avatar_url; - j["is_invite"] = info.is_invite; - j["join_rule"] = info.join_rule; - j["guest_access"] = info.guest_access; - - if (info.member_count != 0) - j["member_count"] = info.member_count; -} - -inline void -from_json(const json &j, RoomInfo &info) -{ - info.name = j.at("name"); - info.topic = j.at("topic"); - info.avatar_url = j.at("avatar_url"); - info.is_invite = j.at("is_invite"); - info.join_rule = j.at("join_rule"); - info.guest_access = j.at("guest_access"); - - if (j.count("member_count")) - info.member_count = j.at("member_count"); -} - -//! Basic information per member; -struct MemberInfo -{ - std::string name; - std::string avatar_url; -}; - -inline void -to_json(json &j, const MemberInfo &info) -{ - j["name"] = info.name; - j["avatar_url"] = info.avatar_url; -} - -inline void -from_json(const json &j, MemberInfo &info) -{ - info.name = j.at("name"); - info.avatar_url = j.at("avatar_url"); -} - -struct RoomSearchResult -{ - std::string room_id; - RoomInfo info; - QImage img; -}; - -Q_DECLARE_METATYPE(RoomSearchResult) -Q_DECLARE_METATYPE(RoomInfo) - -// Extra information associated with an outbound megolm session. -struct OutboundGroupSessionData -{ - std::string session_id; - std::string session_key; - uint64_t message_index = 0; -}; - -inline 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; -} - -inline void -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"); -} - -struct OutboundGroupSessionDataRef -{ - OlmOutboundGroupSession *session; - OutboundGroupSessionData data; -}; - -struct DevicePublicKeys -{ - std::string ed25519; - std::string curve25519; -}; - -inline void -to_json(nlohmann::json &obj, const DevicePublicKeys &msg) -{ - obj["ed25519"] = msg.ed25519; - obj["curve25519"] = msg.curve25519; -} - -inline void -from_json(const nlohmann::json &obj, DevicePublicKeys &msg) -{ - msg.ed25519 = obj.at("ed25519"); - msg.curve25519 = obj.at("curve25519"); -} - -//! Represents a unique megolm session identifier. -struct MegolmSessionIndex -{ - //! The room in which this session exists. - std::string room_id; - //! The session_id of the megolm session. - std::string session_id; - //! The curve25519 public key of the sender. - std::string sender_key; - - //! Representation to be used in a hash map. - std::string to_hash() const { return room_id + session_id + sender_key; } -}; - -struct OlmSessionStorage -{ - // Megolm sessions - std::map<std::string, mtx::crypto::InboundGroupSessionPtr> group_inbound_sessions; - std::map<std::string, mtx::crypto::OutboundGroupSessionPtr> group_outbound_sessions; - std::map<std::string, OutboundGroupSessionData> group_outbound_session_data; - - // Guards for accessing megolm sessions. - std::mutex group_outbound_mtx; - std::mutex group_inbound_mtx; -}; - -class Cache : public QObject -{ - Q_OBJECT - -public: - Cache(const QString &userId, QObject *parent = nullptr); - - static QHash<QString, QString> DisplayNames; - static QHash<QString, QString> AvatarUrls; - - static std::string displayName(const std::string &room_id, const std::string &user_id); - static QString displayName(const QString &room_id, const QString &user_id); - static QString avatarUrl(const QString &room_id, const QString &user_id); - - static void removeDisplayName(const QString &room_id, const QString &user_id); - static void removeAvatarUrl(const QString &room_id, const QString &user_id); - - static void insertDisplayName(const QString &room_id, - const QString &user_id, - const QString &display_name); - static void insertAvatarUrl(const QString &room_id, - const QString &user_id, - const QString &avatar_url); - - //! Load saved data for the display names & avatars. - void populateMembers(); - std::vector<std::string> joinedRooms(); - - QMap<QString, RoomInfo> roomInfo(bool withInvites = true); - std::map<QString, bool> invites(); - - //! Calculate & return the name of the room. - QString getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); - //! Get room join rules - JoinRule getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb); - bool getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb); - //! Retrieve the topic of the room if any. - QString getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); - //! Retrieve the room avatar's url if any. - QString getRoomAvatarUrl(lmdb::txn &txn, - lmdb::dbi &statesdb, - lmdb::dbi &membersdb, - const QString &room_id); - - //! Retrieve member info from a room. - std::vector<RoomMember> getMembers(const std::string &room_id, - std::size_t startIndex = 0, - std::size_t len = 30); - - void saveState(const mtx::responses::Sync &res); - bool isInitialized() const; - - std::string nextBatchToken() const; - - void deleteData(); - - void removeInvite(lmdb::txn &txn, const std::string &room_id); - void removeInvite(const std::string &room_id); - void removeRoom(lmdb::txn &txn, const std::string &roomid); - void removeRoom(const std::string &roomid); - void removeRoom(const QString &roomid) { removeRoom(roomid.toStdString()); }; - void setup(); - - bool isFormatValid(); - void setCurrentFormat(); - - std::map<QString, mtx::responses::Timeline> roomMessages(); - - //! Retrieve all the user ids from a room. - std::vector<std::string> roomMembers(const std::string &room_id); - - //! Check if the given user has power leve greater than than - //! lowest power level of the given events. - bool hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes, - const std::string &room_id, - const std::string &user_id); - - //! Retrieves the saved room avatar. - QImage getRoomAvatar(const QString &id); - QImage getRoomAvatar(const std::string &id); - - //! Adds a user to the read list for the given event. - //! - //! There should be only one user id present in a receipt list per room. - //! The user id should be removed from any other lists. - using Receipts = std::map<std::string, std::map<std::string, uint64_t>>; - void updateReadReceipt(lmdb::txn &txn, - const std::string &room_id, - const Receipts &receipts); - - //! Retrieve all the read receipts for the given event id and room. - //! - //! Returns a map of user ids and the time of the read receipt in milliseconds. - using UserReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>; - UserReceipts readReceipts(const QString &event_id, const QString &room_id); - - QByteArray image(const QString &url) const; - QByteArray image(lmdb::txn &txn, const std::string &url) const; - QByteArray image(const std::string &url) const - { - return image(QString::fromStdString(url)); - } - void saveImage(const std::string &url, const std::string &data); - void saveImage(const QString &url, const QByteArray &data); - - RoomInfo singleRoomInfo(const std::string &room_id); - std::vector<std::string> roomsWithStateUpdates(const mtx::responses::Sync &res); - std::map<QString, RoomInfo> getRoomInfo(const std::vector<std::string> &rooms); - std::map<QString, RoomInfo> roomUpdates(const mtx::responses::Sync &sync) - { - return getRoomInfo(roomsWithStateUpdates(sync)); - } - - QVector<SearchResult> searchUsers(const std::string &room_id, - const std::string &query, - std::uint8_t max_items = 5); - std::vector<RoomSearchResult> searchRooms(const std::string &query, - std::uint8_t max_items = 5); - - void markSentNotification(const std::string &event_id); - //! Removes an event from the sent notifications. - void removeReadNotification(const std::string &event_id); - //! Check if we have sent a desktop notification for the given event id. - bool isNotificationSent(const std::string &event_id); - - //! Mark a room that uses e2e encryption. - void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id); - bool isRoomEncrypted(const std::string &room_id); - - //! Save the public keys for a device. - void saveDeviceKeys(const std::string &device_id); - void getDeviceKeys(const std::string &device_id); - - //! Save the device list for a user. - void setDeviceList(const std::string &user_id, const std::vector<std::string> &devices); - std::vector<std::string> getDeviceList(const std::string &user_id); - - // - // Outbound Megolm Sessions - // - void saveOutboundMegolmSession(const std::string &room_id, - const OutboundGroupSessionData &data, - mtx::crypto::OutboundGroupSessionPtr session); - OutboundGroupSessionDataRef getOutboundMegolmSession(const std::string &room_id); - bool outboundMegolmSessionExists(const std::string &room_id) noexcept; - void updateOutboundMegolmSession(const std::string &room_id, int message_index); - - // - // Inbound Megolm Sessions - // - void saveInboundMegolmSession(const MegolmSessionIndex &index, - mtx::crypto::InboundGroupSessionPtr session); - OlmInboundGroupSession *getInboundMegolmSession(const MegolmSessionIndex &index); - bool inboundMegolmSessionExists(const MegolmSessionIndex &index) noexcept; - - // - // Olm Sessions - // - void saveOlmSession(const std::string &curve25519, mtx::crypto::OlmSessionPtr session); - std::vector<std::string> getOlmSessions(const std::string &curve25519); - boost::optional<mtx::crypto::OlmSessionPtr> getOlmSession(const std::string &curve25519, - const std::string &session_id); - - void saveOlmAccount(const std::string &pickled); - std::string restoreOlmAccount(); - - void restoreSessions(); - - OlmSessionStorage session_storage; - -private: - //! Save an invited room. - void saveInvite(lmdb::txn &txn, - lmdb::dbi &statesdb, - lmdb::dbi &membersdb, - const mtx::responses::InvitedRoom &room); - - QString getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); - QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb); - QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); - - DescInfo getLastMessageInfo(lmdb::txn &txn, const std::string &room_id); - void saveTimelineMessages(lmdb::txn &txn, - const std::string &room_id, - const mtx::responses::Timeline &res); - - mtx::responses::Timeline getTimelineMessages(lmdb::txn &txn, const std::string &room_id); - - //! Remove a room from the cache. - // void removeLeftRoom(lmdb::txn &txn, const std::string &room_id); - template<class T> - void saveStateEvents(lmdb::txn &txn, - const lmdb::dbi &statesdb, - const lmdb::dbi &membersdb, - const std::string &room_id, - const std::vector<T> &events) - { - for (const auto &e : events) - saveStateEvent(txn, statesdb, membersdb, room_id, e); - } - - template<class T> - void saveStateEvent(lmdb::txn &txn, - const lmdb::dbi &statesdb, - const lmdb::dbi &membersdb, - const std::string &room_id, - const T &event) - { - using namespace mtx::events; - using namespace mtx::events::state; - - if (mpark::holds_alternative<StateEvent<Member>>(event)) { - const auto e = mpark::get<StateEvent<Member>>(event); - - switch (e.content.membership) { - // - // We only keep users with invite or join membership. - // - case Membership::Invite: - case Membership::Join: { - auto display_name = e.content.display_name.empty() - ? e.state_key - : e.content.display_name; - - // Lightweight representation of a member. - MemberInfo tmp{display_name, e.content.avatar_url}; - - lmdb::dbi_put(txn, - membersdb, - lmdb::val(e.state_key), - lmdb::val(json(tmp).dump())); - - insertDisplayName(QString::fromStdString(room_id), - QString::fromStdString(e.state_key), - QString::fromStdString(display_name)); - - insertAvatarUrl(QString::fromStdString(room_id), - QString::fromStdString(e.state_key), - QString::fromStdString(e.content.avatar_url)); - - break; - } - default: { - lmdb::dbi_del( - txn, membersdb, lmdb::val(e.state_key), lmdb::val("")); - - removeDisplayName(QString::fromStdString(room_id), - QString::fromStdString(e.state_key)); - removeAvatarUrl(QString::fromStdString(room_id), - QString::fromStdString(e.state_key)); - - break; - } - } - - return; - } else if (mpark::holds_alternative<StateEvent<Encryption>>(event)) { - setEncryptedRoom(txn, room_id); - return; - } - - if (!isStateEvent(event)) - return; - - mpark::visit( - [&txn, &statesdb](auto e) { - lmdb::dbi_put( - txn, statesdb, lmdb::val(to_string(e.type)), lmdb::val(json(e).dump())); - }, - event); - } - - template<class T> - bool isStateEvent(const T &e) - { - using namespace mtx::events; - using namespace mtx::events::state; - - return mpark::holds_alternative<StateEvent<Aliases>>(e) || - mpark::holds_alternative<StateEvent<state::Avatar>>(e) || - mpark::holds_alternative<StateEvent<CanonicalAlias>>(e) || - mpark::holds_alternative<StateEvent<Create>>(e) || - mpark::holds_alternative<StateEvent<GuestAccess>>(e) || - mpark::holds_alternative<StateEvent<HistoryVisibility>>(e) || - mpark::holds_alternative<StateEvent<JoinRules>>(e) || - mpark::holds_alternative<StateEvent<Name>>(e) || - mpark::holds_alternative<StateEvent<Member>>(e) || - mpark::holds_alternative<StateEvent<PowerLevels>>(e) || - mpark::holds_alternative<StateEvent<Topic>>(e); - } - - template<class T> - bool containsStateUpdates(const T &e) - { - using namespace mtx::events; - using namespace mtx::events::state; - - return mpark::holds_alternative<StateEvent<state::Avatar>>(e) || - mpark::holds_alternative<StateEvent<CanonicalAlias>>(e) || - mpark::holds_alternative<StateEvent<Name>>(e) || - mpark::holds_alternative<StateEvent<Member>>(e) || - mpark::holds_alternative<StateEvent<Topic>>(e); - } - - bool containsStateUpdates(const mtx::events::collections::StrippedEvents &e) - { - using namespace mtx::events; - using namespace mtx::events::state; - - return mpark::holds_alternative<StrippedEvent<state::Avatar>>(e) || - mpark::holds_alternative<StrippedEvent<CanonicalAlias>>(e) || - mpark::holds_alternative<StrippedEvent<Name>>(e) || - mpark::holds_alternative<StrippedEvent<Member>>(e) || - mpark::holds_alternative<StrippedEvent<Topic>>(e); - } - - void saveInvites(lmdb::txn &txn, - const std::map<std::string, mtx::responses::InvitedRoom> &rooms); - - //! Sends signals for the rooms that are removed. - void removeLeftRooms(lmdb::txn &txn, - const std::map<std::string, mtx::responses::LeftRoom> &rooms) - { - for (const auto &room : rooms) { - removeRoom(txn, room.first); - - // Clean up leftover invites. - removeInvite(txn, room.first); - } - } - - lmdb::dbi getMessagesDb(lmdb::txn &txn, const std::string &room_id) - { - auto db = - lmdb::dbi::open(txn, std::string(room_id + "/messages").c_str(), MDB_CREATE); - lmdb::dbi_set_compare(txn, db, numeric_key_comparison); - - return db; - } - - lmdb::dbi getInviteStatesDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open( - txn, std::string(room_id + "/invite_state").c_str(), MDB_CREATE); - } - - lmdb::dbi getInviteMembersDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open( - txn, std::string(room_id + "/invite_members").c_str(), MDB_CREATE); - } - - lmdb::dbi getStatesDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open(txn, std::string(room_id + "/state").c_str(), MDB_CREATE); - } - - lmdb::dbi getMembersDb(lmdb::txn &txn, const std::string &room_id) - { - return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE); - } - - //! Retrieves or creates the database that stores the open OLM sessions between our device - //! and the given curve25519 key which represents another device. - //! - //! Each entry is a map from the session_id to the pickled representation of the session. - lmdb::dbi getOlmSessionsDb(lmdb::txn &txn, const std::string &curve25519_key) - { - return lmdb::dbi::open( - txn, std::string("olm_sessions/" + curve25519_key).c_str(), MDB_CREATE); - } - - QString getDisplayName(const mtx::events::StateEvent<mtx::events::state::Member> &event) - { - if (!event.content.display_name.empty()) - return QString::fromStdString(event.content.display_name); - - return QString::fromStdString(event.state_key); - } - - void setNextBatchToken(lmdb::txn &txn, const std::string &token); - void setNextBatchToken(lmdb::txn &txn, const QString &token); - - lmdb::env env_; - lmdb::dbi syncStateDb_; - lmdb::dbi roomsDb_; - lmdb::dbi invitesDb_; - lmdb::dbi mediaDb_; - lmdb::dbi readReceiptsDb_; - lmdb::dbi notificationsDb_; - - lmdb::dbi devicesDb_; - lmdb::dbi deviceKeysDb_; - - lmdb::dbi inboundMegolmSessionDb_; - lmdb::dbi outboundMegolmSessionDb_; - - QString localUserId_; - QString cacheDirectory_; -}; - -namespace cache { -void -init(const QString &user_id); - -Cache * -client(); -}