2017-07-29 11:49:00 +03:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
2018-06-15 01:35:31 +03:00
|
|
|
#include <boost/optional.hpp>
|
|
|
|
|
2018-06-28 16:16:43 +03:00
|
|
|
#include <QDateTime>
|
2017-08-26 14:31:23 +03:00
|
|
|
#include <QDir>
|
2018-04-29 15:42:40 +03:00
|
|
|
#include <QImage>
|
2018-06-28 16:16:43 +03:00
|
|
|
#include <QString>
|
2018-06-10 20:03:45 +03:00
|
|
|
|
2017-07-29 11:49:00 +03:00
|
|
|
#include <lmdb++.h>
|
2018-05-13 01:31:58 +03:00
|
|
|
#include <mtx/events/join_rules.hpp>
|
2017-12-19 23:36:12 +03:00
|
|
|
#include <mtx/responses.hpp>
|
2018-06-10 20:03:45 +03:00
|
|
|
#include <mtxclient/crypto/client.hpp>
|
|
|
|
#include <mutex>
|
2019-02-25 06:10:02 +03:00
|
|
|
#include <nlohmann/json.hpp>
|
2018-06-10 20:03:45 +03:00
|
|
|
|
2018-07-17 16:37:25 +03:00
|
|
|
#include "Logging.h"
|
2018-06-18 18:36:19 +03:00
|
|
|
|
2018-05-13 01:31:58 +03:00
|
|
|
using mtx::events::state::JoinRule;
|
2017-07-29 11:49:00 +03:00
|
|
|
|
2018-05-01 19:35:28 +03:00
|
|
|
struct RoomMember
|
|
|
|
{
|
|
|
|
QString user_id;
|
|
|
|
QString display_name;
|
|
|
|
QImage avatar;
|
|
|
|
};
|
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
struct SearchResult
|
|
|
|
{
|
|
|
|
QString user_id;
|
|
|
|
QString display_name;
|
|
|
|
};
|
|
|
|
|
2018-06-30 22:43:55 +03:00
|
|
|
static int
|
2018-06-28 16:16:43 +03:00
|
|
|
numeric_key_comparison(const MDB_val *a, const MDB_val *b)
|
|
|
|
{
|
2018-06-30 22:43:55 +03:00
|
|
|
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));
|
2018-06-28 16:16:43 +03:00
|
|
|
|
|
|
|
if (lhs < rhs)
|
|
|
|
return 1;
|
|
|
|
else if (lhs == rhs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
Q_DECLARE_METATYPE(SearchResult)
|
|
|
|
Q_DECLARE_METATYPE(QVector<SearchResult>)
|
2018-05-01 19:49:01 +03:00
|
|
|
Q_DECLARE_METATYPE(RoomMember)
|
2018-06-28 16:16:43 +03:00
|
|
|
Q_DECLARE_METATYPE(mtx::responses::Timeline)
|
2017-07-29 11:49:00 +03:00
|
|
|
|
2018-01-03 19:05:49 +03:00
|
|
|
//! 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>();
|
|
|
|
}
|
|
|
|
|
2018-06-28 16:16:43 +03:00
|
|
|
struct DescInfo
|
|
|
|
{
|
2018-09-13 19:15:58 +03:00
|
|
|
QString event_id;
|
2018-06-28 16:16:43 +03:00
|
|
|
QString username;
|
|
|
|
QString userid;
|
|
|
|
QString body;
|
|
|
|
QString timestamp;
|
|
|
|
QDateTime datetime;
|
|
|
|
};
|
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
//! 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;
|
2019-06-27 21:53:44 +03:00
|
|
|
//! The calculated version of this room set at creation time.
|
|
|
|
std::string version;
|
2018-04-21 16:34:50 +03:00
|
|
|
//! Whether or not the room is an invite.
|
|
|
|
bool is_invite = false;
|
2018-04-30 21:41:47 +03:00
|
|
|
//! Total number of members in the room.
|
|
|
|
int16_t member_count = 0;
|
2018-05-13 01:31:58 +03:00
|
|
|
//! Who can access to the room.
|
|
|
|
JoinRule join_rule = JoinRule::Public;
|
|
|
|
bool guest_access = false;
|
2018-06-28 16:16:43 +03:00
|
|
|
//! Metadata describing the last message in the timeline.
|
|
|
|
DescInfo msgInfo;
|
2018-09-28 15:40:51 +03:00
|
|
|
//! The list of tags associated with this room
|
|
|
|
std::vector<std::string> tags;
|
2018-04-21 16:34:50 +03:00
|
|
|
};
|
|
|
|
|
2019-06-27 21:53:44 +03:00
|
|
|
void
|
|
|
|
to_json(json &j, const RoomInfo &info);
|
2018-04-21 16:34:50 +03:00
|
|
|
|
2019-06-27 21:53:44 +03:00
|
|
|
void
|
|
|
|
from_json(const json &j, RoomInfo &info);
|
2018-04-21 16:34:50 +03:00
|
|
|
|
|
|
|
//! 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");
|
|
|
|
}
|
|
|
|
|
2018-04-27 01:57:46 +03:00
|
|
|
struct RoomSearchResult
|
|
|
|
{
|
|
|
|
std::string room_id;
|
|
|
|
RoomInfo info;
|
|
|
|
QImage img;
|
|
|
|
};
|
|
|
|
|
|
|
|
Q_DECLARE_METATYPE(RoomSearchResult)
|
2018-04-21 16:34:50 +03:00
|
|
|
Q_DECLARE_METATYPE(RoomInfo)
|
|
|
|
|
2018-06-10 20:03:45 +03:00
|
|
|
// 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;
|
|
|
|
};
|
|
|
|
|
2018-09-15 23:52:14 +03:00
|
|
|
inline void
|
|
|
|
to_json(nlohmann::json &obj, const MegolmSessionIndex &msg)
|
|
|
|
{
|
|
|
|
obj["room_id"] = msg.room_id;
|
|
|
|
obj["session_id"] = msg.session_id;
|
|
|
|
obj["sender_key"] = msg.sender_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
from_json(const nlohmann::json &obj, MegolmSessionIndex &msg)
|
|
|
|
{
|
|
|
|
msg.room_id = obj.at("room_id");
|
|
|
|
msg.session_id = obj.at("session_id");
|
|
|
|
msg.sender_key = obj.at("sender_key");
|
|
|
|
}
|
|
|
|
|
2018-06-10 20:03:45 +03:00
|
|
|
struct OlmSessionStorage
|
|
|
|
{
|
2018-06-15 01:35:31 +03:00
|
|
|
// Megolm sessions
|
2018-06-10 20:03:45 +03:00
|
|
|
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;
|
|
|
|
|
2018-06-15 01:35:31 +03:00
|
|
|
// Guards for accessing megolm sessions.
|
2018-06-10 20:03:45 +03:00
|
|
|
std::mutex group_outbound_mtx;
|
|
|
|
std::mutex group_inbound_mtx;
|
|
|
|
};
|
|
|
|
|
2018-01-21 22:43:21 +03:00
|
|
|
class Cache : public QObject
|
2017-07-29 11:49:00 +03:00
|
|
|
{
|
2018-01-21 22:43:21 +03:00
|
|
|
Q_OBJECT
|
|
|
|
|
2017-07-29 11:49:00 +03:00
|
|
|
public:
|
2018-01-21 22:43:21 +03:00
|
|
|
Cache(const QString &userId, QObject *parent = nullptr);
|
2017-07-29 11:49:00 +03:00
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
static QHash<QString, QString> DisplayNames;
|
|
|
|
static QHash<QString, QString> AvatarUrls;
|
2019-01-26 05:53:43 +03:00
|
|
|
static QHash<QString, QString> UserColors;
|
2018-04-21 16:34:50 +03:00
|
|
|
|
|
|
|
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);
|
2019-01-26 05:53:43 +03:00
|
|
|
static QString userColor(const QString &user_id);
|
2018-04-21 16:34:50 +03:00
|
|
|
|
|
|
|
static void removeDisplayName(const QString &room_id, const QString &user_id);
|
|
|
|
static void removeAvatarUrl(const QString &room_id, const QString &user_id);
|
2019-01-26 05:53:43 +03:00
|
|
|
static void removeUserColor(const QString &user_id);
|
2018-04-21 16:34:50 +03:00
|
|
|
|
|
|
|
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);
|
2019-01-26 05:53:43 +03:00
|
|
|
static void insertUserColor(const QString &user_id, const QString &color_name);
|
|
|
|
|
|
|
|
static void clearUserColors();
|
2018-04-21 16:34:50 +03:00
|
|
|
|
|
|
|
//! Load saved data for the display names & avatars.
|
|
|
|
void populateMembers();
|
|
|
|
std::vector<std::string> joinedRooms();
|
|
|
|
|
2018-04-22 11:27:00 +03:00
|
|
|
QMap<QString, RoomInfo> roomInfo(bool withInvites = true);
|
2018-04-22 14:19:05 +03:00
|
|
|
std::map<QString, bool> invites();
|
2018-04-21 16:34:50 +03:00
|
|
|
|
|
|
|
//! Calculate & return the name of the room.
|
|
|
|
QString getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
|
2018-05-13 01:31:58 +03:00
|
|
|
//! Get room join rules
|
|
|
|
JoinRule getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb);
|
|
|
|
bool getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb);
|
2018-04-21 16:34:50 +03:00
|
|
|
//! 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);
|
2019-06-27 21:53:44 +03:00
|
|
|
//! Retrieve the version of the room if any.
|
|
|
|
QString getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb);
|
2018-04-21 16:34:50 +03:00
|
|
|
|
2018-05-01 19:35:28 +03:00
|
|
|
//! 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);
|
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
void saveState(const mtx::responses::Sync &res);
|
2017-08-26 13:49:16 +03:00
|
|
|
bool isInitialized() const;
|
2017-07-29 11:49:00 +03:00
|
|
|
|
2018-06-09 16:03:14 +03:00
|
|
|
std::string nextBatchToken() const;
|
2017-12-19 23:36:12 +03:00
|
|
|
|
2017-10-22 19:03:55 +03:00
|
|
|
void deleteData();
|
2017-07-29 11:49:00 +03:00
|
|
|
|
2018-04-22 12:26:41 +03:00
|
|
|
void removeInvite(lmdb::txn &txn, const std::string &room_id);
|
2018-04-21 16:34:50 +03:00
|
|
|
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()); };
|
2017-10-03 21:16:31 +03:00
|
|
|
void setup();
|
2017-10-01 19:49:36 +03:00
|
|
|
|
2017-12-10 13:51:44 +03:00
|
|
|
bool isFormatValid();
|
|
|
|
void setCurrentFormat();
|
|
|
|
|
2018-06-28 16:16:43 +03:00
|
|
|
std::map<QString, mtx::responses::Timeline> roomMessages();
|
|
|
|
|
2019-08-10 06:34:44 +03:00
|
|
|
std::map<QString, mtx::responses::Notifications> getTimelineMentions();
|
|
|
|
|
2018-06-13 12:28:00 +03:00
|
|
|
//! Retrieve all the user ids from a room.
|
|
|
|
std::vector<std::string> roomMembers(const std::string &room_id);
|
|
|
|
|
2018-05-16 23:30:50 +03:00
|
|
|
//! 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);
|
|
|
|
|
2018-04-24 14:13:05 +03:00
|
|
|
//! Retrieves the saved room avatar.
|
|
|
|
QImage getRoomAvatar(const QString &id);
|
2018-05-05 22:40:24 +03:00
|
|
|
QImage getRoomAvatar(const std::string &id);
|
2018-04-24 14:13:05 +03:00
|
|
|
|
2018-01-03 19:05:49 +03:00
|
|
|
//! 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>>;
|
2018-04-21 17:14:16 +03:00
|
|
|
void updateReadReceipt(lmdb::txn &txn,
|
|
|
|
const std::string &room_id,
|
|
|
|
const Receipts &receipts);
|
2018-01-03 19:05:49 +03:00
|
|
|
|
|
|
|
//! 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.
|
2018-01-15 00:33:12 +03:00
|
|
|
using UserReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>;
|
2018-01-03 19:05:49 +03:00
|
|
|
UserReceipts readReceipts(const QString &event_id, const QString &room_id);
|
|
|
|
|
2018-07-17 23:50:18 +03:00
|
|
|
//! Filter the events that have at least one read receipt.
|
|
|
|
std::vector<QString> filterReadEvents(const QString &room_id,
|
|
|
|
const std::vector<QString> &event_ids,
|
|
|
|
const std::string &excluded_user);
|
|
|
|
//! Add event for which we are expecting some read receipts.
|
|
|
|
void addPendingReceipt(const QString &room_id, const QString &event_id);
|
|
|
|
void removePendingReceipt(lmdb::txn &txn,
|
|
|
|
const std::string &room_id,
|
|
|
|
const std::string &event_id);
|
2018-09-13 19:15:58 +03:00
|
|
|
void notifyForReadReceipts(const std::string &room_id);
|
2018-07-17 23:50:18 +03:00
|
|
|
std::vector<QString> pendingReceiptsEvents(lmdb::txn &txn, const std::string &room_id);
|
|
|
|
|
2017-12-22 01:00:48 +03:00
|
|
|
QByteArray image(const QString &url) const;
|
2018-04-27 01:57:46 +03:00
|
|
|
QByteArray image(lmdb::txn &txn, const std::string &url) const;
|
|
|
|
QByteArray image(const std::string &url) const
|
|
|
|
{
|
|
|
|
return image(QString::fromStdString(url));
|
|
|
|
}
|
2018-06-09 16:03:14 +03:00
|
|
|
void saveImage(const std::string &url, const std::string &data);
|
2017-12-22 01:00:48 +03:00
|
|
|
void saveImage(const QString &url, const QByteArray &data);
|
|
|
|
|
2018-05-05 22:40:24 +03:00
|
|
|
RoomInfo singleRoomInfo(const std::string &room_id);
|
2018-04-21 16:34:50 +03:00
|
|
|
std::vector<std::string> roomsWithStateUpdates(const mtx::responses::Sync &res);
|
2018-09-28 15:40:51 +03:00
|
|
|
std::vector<std::string> roomsWithTagUpdates(const mtx::responses::Sync &res);
|
2018-04-21 16:34:50 +03:00
|
|
|
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));
|
|
|
|
}
|
2018-09-28 15:40:51 +03:00
|
|
|
std::map<QString, RoomInfo> roomTagUpdates(const mtx::responses::Sync &sync)
|
|
|
|
{
|
|
|
|
return getRoomInfo(roomsWithTagUpdates(sync));
|
|
|
|
}
|
2018-04-21 16:34:50 +03:00
|
|
|
|
2018-09-13 19:15:58 +03:00
|
|
|
//! Calculates which the read status of a room.
|
|
|
|
//! Whether all the events in the timeline have been read.
|
|
|
|
bool calculateRoomReadStatus(const std::string &room_id);
|
|
|
|
void calculateRoomReadStatus();
|
|
|
|
|
2018-04-27 01:57:46 +03:00
|
|
|
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);
|
2018-01-21 22:43:21 +03:00
|
|
|
|
2018-05-05 16:38:41 +03:00
|
|
|
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);
|
|
|
|
|
2019-08-10 06:34:44 +03:00
|
|
|
//! Add all notifications containing a user mention to the db.
|
|
|
|
void saveTimelineMentions(const mtx::responses::Notifications &res);
|
2019-08-06 06:00:07 +03:00
|
|
|
|
2018-08-25 21:08:43 +03:00
|
|
|
//! Remove old unused data.
|
|
|
|
void deleteOldMessages();
|
|
|
|
void deleteOldData() noexcept;
|
|
|
|
//! Retrieve all saved room ids.
|
|
|
|
std::vector<std::string> getRoomIds(lmdb::txn &txn);
|
|
|
|
|
2018-06-10 20:03:45 +03:00
|
|
|
//! Mark a room that uses e2e encryption.
|
2018-06-18 18:36:19 +03:00
|
|
|
void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id);
|
2018-06-12 09:45:26 +03:00
|
|
|
bool isRoomEncrypted(const std::string &room_id);
|
|
|
|
|
2018-06-10 20:03:45 +03:00
|
|
|
//! 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);
|
|
|
|
|
2018-07-21 21:40:11 +03:00
|
|
|
//! Check if a user is a member of the room.
|
|
|
|
bool isRoomMember(const std::string &user_id, const std::string &room_id);
|
|
|
|
|
2018-06-10 20:03:45 +03:00
|
|
|
//
|
|
|
|
// Outbound Megolm Sessions
|
|
|
|
//
|
2018-06-12 22:35:10 +03:00
|
|
|
void saveOutboundMegolmSession(const std::string &room_id,
|
2018-06-10 20:03:45 +03:00
|
|
|
const OutboundGroupSessionData &data,
|
|
|
|
mtx::crypto::OutboundGroupSessionPtr session);
|
2018-06-13 12:28:00 +03:00
|
|
|
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);
|
2018-06-10 20:03:45 +03:00
|
|
|
|
2018-09-15 23:52:14 +03:00
|
|
|
void importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys);
|
|
|
|
mtx::crypto::ExportedSessionKeys exportSessionKeys();
|
|
|
|
|
2018-06-10 20:03:45 +03:00
|
|
|
//
|
|
|
|
// Inbound Megolm Sessions
|
|
|
|
//
|
|
|
|
void saveInboundMegolmSession(const MegolmSessionIndex &index,
|
|
|
|
mtx::crypto::InboundGroupSessionPtr session);
|
|
|
|
OlmInboundGroupSession *getInboundMegolmSession(const MegolmSessionIndex &index);
|
2018-09-15 23:52:14 +03:00
|
|
|
bool inboundMegolmSessionExists(const MegolmSessionIndex &index);
|
2018-06-10 20:03:45 +03:00
|
|
|
|
|
|
|
//
|
2018-06-15 01:35:31 +03:00
|
|
|
// Olm Sessions
|
2018-06-10 20:03:45 +03:00
|
|
|
//
|
2018-06-15 01:35:31 +03:00
|
|
|
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);
|
2018-06-10 20:03:45 +03:00
|
|
|
|
|
|
|
void saveOlmAccount(const std::string &pickled);
|
|
|
|
std::string restoreOlmAccount();
|
|
|
|
|
|
|
|
void restoreSessions();
|
|
|
|
|
|
|
|
OlmSessionStorage session_storage;
|
|
|
|
|
2018-07-17 23:50:18 +03:00
|
|
|
signals:
|
|
|
|
void newReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
2018-09-13 19:15:58 +03:00
|
|
|
void roomReadStatus(const std::map<QString, bool> &status);
|
2018-07-17 23:50:18 +03:00
|
|
|
|
2017-07-29 11:49:00 +03:00
|
|
|
private:
|
2018-04-21 16:34:50 +03:00
|
|
|
//! Save an invited room.
|
|
|
|
void saveInvite(lmdb::txn &txn,
|
|
|
|
lmdb::dbi &statesdb,
|
|
|
|
lmdb::dbi &membersdb,
|
|
|
|
const mtx::responses::InvitedRoom &room);
|
|
|
|
|
2019-08-10 06:34:44 +03:00
|
|
|
//! Add a notification containing a user mention to the db.
|
|
|
|
void saveTimelineMentions(lmdb::txn &txn,
|
|
|
|
const std::string &room_id,
|
|
|
|
const QList<mtx::responses::Notification> &res);
|
|
|
|
|
|
|
|
//! Get timeline items that a user was mentions in for a given room
|
|
|
|
mtx::responses::Notifications getTimelineMentionsForRoom(lmdb::txn &txn,
|
2019-08-10 06:36:45 +03:00
|
|
|
const std::string &room_id);
|
2019-08-10 06:34:44 +03:00
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
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);
|
|
|
|
|
2018-06-28 16:16:43 +03:00
|
|
|
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);
|
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
//! 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;
|
|
|
|
|
2018-09-01 13:35:10 +03:00
|
|
|
if (boost::get<StateEvent<Member>>(&event) != nullptr) {
|
|
|
|
const auto e = boost::get<StateEvent<Member>>(event);
|
2018-04-21 16:34:50 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-18 18:36:19 +03:00
|
|
|
return;
|
2018-09-01 13:35:10 +03:00
|
|
|
} else if (boost::get<StateEvent<Encryption>>(&event) != nullptr) {
|
2018-06-18 18:36:19 +03:00
|
|
|
setEncryptedRoom(txn, room_id);
|
2018-04-21 16:34:50 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isStateEvent(event))
|
|
|
|
return;
|
|
|
|
|
2018-09-01 13:35:10 +03:00
|
|
|
boost::apply_visitor(
|
2018-04-21 16:34:50 +03:00
|
|
|
[&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;
|
|
|
|
|
2018-09-01 13:35:10 +03:00
|
|
|
return boost::get<StateEvent<Aliases>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<state::Avatar>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<CanonicalAlias>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<Create>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<GuestAccess>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<HistoryVisibility>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<JoinRules>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<Name>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<Member>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<PowerLevels>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<Topic>>(&e) != nullptr;
|
2018-04-21 16:34:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
|
|
bool containsStateUpdates(const T &e)
|
|
|
|
{
|
|
|
|
using namespace mtx::events;
|
|
|
|
using namespace mtx::events::state;
|
|
|
|
|
2018-09-01 13:35:10 +03:00
|
|
|
return boost::get<StateEvent<state::Avatar>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<CanonicalAlias>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<Name>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<Member>>(&e) != nullptr ||
|
|
|
|
boost::get<StateEvent<Topic>>(&e) != nullptr;
|
2018-04-21 16:34:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool containsStateUpdates(const mtx::events::collections::StrippedEvents &e)
|
|
|
|
{
|
|
|
|
using namespace mtx::events;
|
|
|
|
using namespace mtx::events::state;
|
|
|
|
|
2018-09-01 13:35:10 +03:00
|
|
|
return boost::get<StrippedEvent<state::Avatar>>(&e) != nullptr ||
|
|
|
|
boost::get<StrippedEvent<CanonicalAlias>>(&e) != nullptr ||
|
|
|
|
boost::get<StrippedEvent<Name>>(&e) != nullptr ||
|
|
|
|
boost::get<StrippedEvent<Member>>(&e) != nullptr ||
|
|
|
|
boost::get<StrippedEvent<Topic>>(&e) != nullptr;
|
2018-04-21 16:34:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2018-04-22 12:26:41 +03:00
|
|
|
for (const auto &room : rooms) {
|
2018-04-21 16:34:50 +03:00
|
|
|
removeRoom(txn, room.first);
|
2018-04-22 12:26:41 +03:00
|
|
|
|
|
|
|
// Clean up leftover invites.
|
|
|
|
removeInvite(txn, room.first);
|
|
|
|
}
|
2018-04-21 16:34:50 +03:00
|
|
|
}
|
|
|
|
|
2018-07-17 23:50:18 +03:00
|
|
|
lmdb::dbi getPendingReceiptsDb(lmdb::txn &txn)
|
|
|
|
{
|
|
|
|
return lmdb::dbi::open(txn, "pending_receipts", MDB_CREATE);
|
|
|
|
}
|
|
|
|
|
2018-06-28 16:16:43 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-08-06 06:00:07 +03:00
|
|
|
lmdb::dbi getMentionsDb(lmdb::txn &txn, const std::string &room_id)
|
|
|
|
{
|
|
|
|
return lmdb::dbi::open(txn, std::string(room_id + "/mentions").c_str(), MDB_CREATE);
|
|
|
|
}
|
|
|
|
|
2018-06-15 01:35:31 +03:00
|
|
|
//! 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);
|
|
|
|
}
|
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
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);
|
2017-08-26 13:49:16 +03:00
|
|
|
void setNextBatchToken(lmdb::txn &txn, const QString &token);
|
2017-07-29 11:49:00 +03:00
|
|
|
|
2017-08-26 13:49:16 +03:00
|
|
|
lmdb::env env_;
|
2018-04-21 16:34:50 +03:00
|
|
|
lmdb::dbi syncStateDb_;
|
|
|
|
lmdb::dbi roomsDb_;
|
2017-12-19 23:36:12 +03:00
|
|
|
lmdb::dbi invitesDb_;
|
2018-04-21 16:34:50 +03:00
|
|
|
lmdb::dbi mediaDb_;
|
2018-01-03 19:05:49 +03:00
|
|
|
lmdb::dbi readReceiptsDb_;
|
2018-05-05 16:38:41 +03:00
|
|
|
lmdb::dbi notificationsDb_;
|
2017-07-29 11:49:00 +03:00
|
|
|
|
2018-06-10 20:03:45 +03:00
|
|
|
lmdb::dbi devicesDb_;
|
|
|
|
lmdb::dbi deviceKeysDb_;
|
|
|
|
|
|
|
|
lmdb::dbi inboundMegolmSessionDb_;
|
|
|
|
lmdb::dbi outboundMegolmSessionDb_;
|
|
|
|
|
2018-04-21 16:34:50 +03:00
|
|
|
QString localUserId_;
|
2017-08-26 14:31:23 +03:00
|
|
|
QString cacheDirectory_;
|
2017-07-29 11:49:00 +03:00
|
|
|
};
|
2018-05-08 20:30:09 +03:00
|
|
|
|
|
|
|
namespace cache {
|
|
|
|
void
|
2018-05-11 13:41:46 +03:00
|
|
|
init(const QString &user_id);
|
2018-05-08 20:30:09 +03:00
|
|
|
|
|
|
|
Cache *
|
|
|
|
client();
|
|
|
|
}
|