From b89257a34b2a98b737f4ae544f7e436b9000b240 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 9 Jun 2018 16:03:14 +0300 Subject: Migrate to mtxclient for the http calls --- src/Logging.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/Logging.cpp (limited to 'src/Logging.cpp') diff --git a/src/Logging.cpp b/src/Logging.cpp new file mode 100644 index 00000000..c6c1c502 --- /dev/null +++ b/src/Logging.cpp @@ -0,0 +1,50 @@ +#include "Logging.hpp" + +#include +#include + +namespace { +std::shared_ptr db_logger = nullptr; +std::shared_ptr net_logger = nullptr; +std::shared_ptr main_logger = nullptr; + +constexpr auto MAX_FILE_SIZE = 1024 * 1024 * 6; +constexpr auto MAX_LOG_FILES = 3; +} + +namespace log { +void +init(const std::string &file_path) +{ + auto file_sink = std::make_shared( + file_path, MAX_FILE_SIZE, MAX_LOG_FILES); + + auto console_sink = std::make_shared(); + + std::vector sinks; + sinks.push_back(file_sink); + sinks.push_back(console_sink); + + net_logger = std::make_shared("net", std::begin(sinks), std::end(sinks)); + main_logger = std::make_shared("main", std::begin(sinks), std::end(sinks)); + db_logger = std::make_shared("db", std::begin(sinks), std::end(sinks)); +} + +std::shared_ptr +main() +{ + return main_logger; +} + +std::shared_ptr +net() +{ + return net_logger; +} + +std::shared_ptr +db() +{ + return db_logger; +} +} -- cgit 1.5.1 From 626c68091126f84819091840a011c50e26dcbd8d Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 10 Jun 2018 20:03:45 +0300 Subject: Add support for displaying decrypted messages --- CMakeLists.txt | 1 + deps/CMakeLists.txt | 2 +- include/Cache.h | 131 ++++++++++++++++++++- include/ChatPage.h | 8 +- include/Logging.hpp | 3 + include/MainWindow.h | 1 - include/MatrixClient.h | 5 +- include/Olm.hpp | 65 +++++++++++ include/timeline/TimelineView.h | 3 + src/Cache.cc | 250 ++++++++++++++++++++++++++++++++++++++-- src/ChatPage.cc | 199 +++++++++++++++++++++++++------- src/CommunitiesList.cc | 3 + src/Logging.cpp | 15 ++- src/MainWindow.cc | 13 +-- src/MatrixClient.cc | 8 +- src/Olm.cpp | 139 ++++++++++++++++++++++ src/RoomList.cc | 2 +- src/main.cc | 8 +- src/timeline/TimelineView.cc | 112 +++++++++++++----- 19 files changed, 869 insertions(+), 99 deletions(-) create mode 100644 include/Olm.hpp create mode 100644 src/Olm.cpp (limited to 'src/Logging.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index eedf9a69..ef20be39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,7 @@ set(SRC_FILES src/MainWindow.cc src/MatrixClient.cc src/QuickSwitcher.cc + src/Olm.cpp src/RegisterPage.cc src/RoomInfoListItem.cc src/RoomList.cc diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index d6bab7e5..5f9b48ca 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -40,7 +40,7 @@ set(MATRIX_STRUCTS_URL https://github.com/mujx/matrix-structs) set(MATRIX_STRUCTS_TAG eeb7373729a1618e2b3838407863342b88b8a0de) set(MTXCLIENT_URL https://github.com/mujx/mtxclient) -set(MTXCLIENT_TAG 57f56d1fe73989dbe041a7ac0a28bf2e3286bf98) +set(MTXCLIENT_TAG 26aad7088b9532808ded9919d55f58711c0138e3) set(OLM_URL https://git.matrix.org/git/olm.git) set(OLM_TAG 4065c8e11a33ba41133a086ed3de4da94dcb6bae) diff --git a/include/Cache.h b/include/Cache.h index afc7a148..994a6da7 100644 --- a/include/Cache.h +++ b/include/Cache.h @@ -17,13 +17,16 @@ #pragma once -#include #include #include + #include #include #include #include +#include +#include + using mtx::events::state::JoinRule; struct RoomMember @@ -140,6 +143,83 @@ struct RoomSearchResult 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 +{ + std::map outbound_sessions; + std::map group_inbound_sessions; + std::map group_outbound_sessions; + std::map group_outbound_session_data; + + // Guards for accessing critical data. + std::mutex outbound_mtx; + std::mutex group_outbound_mtx; + std::mutex group_inbound_mtx; +}; + class Cache : public QObject { Q_OBJECT @@ -260,6 +340,48 @@ public: //! 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(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 &devices); + std::vector getDeviceList(const std::string &user_id); + + // + // Outbound Megolm Sessions + // + void saveOutboundMegolmSession(const MegolmSessionIndex &index, + const OutboundGroupSessionData &data, + mtx::crypto::OutboundGroupSessionPtr session); + OutboundGroupSessionDataRef getOutboundMegolmSession(const MegolmSessionIndex &index); + bool outboundMegolmSessionExists(const MegolmSessionIndex &index) noexcept; + + // + // Inbound Megolm Sessions + // + void saveInboundMegolmSession(const MegolmSessionIndex &index, + mtx::crypto::InboundGroupSessionPtr session); + OlmInboundGroupSession *getInboundMegolmSession(const MegolmSessionIndex &index); + bool inboundMegolmSessionExists(const MegolmSessionIndex &index) noexcept; + + // + // Outbound Olm Sessions + // + void saveOutboundOlmSession(const std::string &curve25519, + mtx::crypto::OlmSessionPtr session); + OlmSession *getOutboundOlmSession(const std::string &curve25519); + bool outboundOlmSessionsExists(const std::string &curve25519) noexcept; + + void saveOlmAccount(const std::string &pickled); + std::string restoreOlmAccount(); + + void restoreSessions(); + + OlmSessionStorage session_storage; + private: //! Save an invited room. void saveInvite(lmdb::txn &txn, @@ -451,6 +573,13 @@ private: lmdb::dbi readReceiptsDb_; lmdb::dbi notificationsDb_; + lmdb::dbi devicesDb_; + lmdb::dbi deviceKeysDb_; + + lmdb::dbi inboundMegolmSessionDb_; + lmdb::dbi outboundMegolmSessionDb_; + lmdb::dbi outboundOlmSessionDb_; + QString localUserId_; QString cacheDirectory_; }; diff --git a/include/ChatPage.h b/include/ChatPage.h index e99e94ba..d8582993 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -29,8 +29,7 @@ #include "Cache.h" #include "CommunitiesList.h" #include "Community.h" - -#include +#include "MatrixClient.h" class OverlayModal; class QuickSwitcher; @@ -119,6 +118,7 @@ signals: void loggedOut(); void trySyncCb(); + void tryDelayedSyncCb(); void tryInitialSyncCb(); void leftRoom(const QString &room_id); @@ -146,8 +146,12 @@ private slots: private: static ChatPage *instance_; + //! Handler callback for initial sync. It doesn't run on the main thread so all + //! communication with the GUI should be done through signals. + void initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err); void tryInitialSync(); void trySync(); + void ensureOneTimeKeyCount(const std::map &counts); //! Check if the given room is currently open. bool isRoomActive(const QString &room_id) diff --git a/include/Logging.hpp b/include/Logging.hpp index c301d80d..bdbd3e2c 100644 --- a/include/Logging.hpp +++ b/include/Logging.hpp @@ -15,4 +15,7 @@ net(); std::shared_ptr db(); + +std::shared_ptr +crypto(); } diff --git a/include/MainWindow.h b/include/MainWindow.h index f0fa9a08..b068e8f6 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -59,7 +59,6 @@ class MainWindow : public QMainWindow public: explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); static MainWindow *instance() { return instance_; }; void saveCurrentWindowSize(); diff --git a/include/MatrixClient.h b/include/MatrixClient.h index 832d6cad..7ea5e0b7 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -11,12 +11,15 @@ Q_DECLARE_METATYPE(mtx::responses::Notifications) Q_DECLARE_METATYPE(mtx::responses::Rooms) Q_DECLARE_METATYPE(mtx::responses::Sync) Q_DECLARE_METATYPE(std::string) -Q_DECLARE_METATYPE(std::vector); +Q_DECLARE_METATYPE(std::vector) namespace http { namespace v2 { mtx::http::Client * client(); + +bool +is_logged_in(); } //! Initialize the http module diff --git a/include/Olm.hpp b/include/Olm.hpp new file mode 100644 index 00000000..2f7b1d64 --- /dev/null +++ b/include/Olm.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +constexpr auto OLM_ALGO = "m.olm.v1.curve25519-aes-sha2"; + +namespace olm { + +struct OlmCipherContent +{ + std::string body; + uint8_t type; +}; + +inline void +from_json(const nlohmann::json &obj, OlmCipherContent &msg) +{ + msg.body = obj.at("body"); + msg.type = obj.at("type"); +} + +struct OlmMessage +{ + std::string sender_key; + std::string sender; + + using RecipientKey = std::string; + std::map ciphertext; +}; + +inline void +from_json(const nlohmann::json &obj, OlmMessage &msg) +{ + if (obj.at("type") != "m.room.encrypted") + throw std::invalid_argument("invalid type for olm message"); + + if (obj.at("content").at("algorithm") != OLM_ALGO) + throw std::invalid_argument("invalid algorithm for olm message"); + + msg.sender = obj.at("sender"); + msg.sender_key = obj.at("content").at("sender_key"); + msg.ciphertext = + obj.at("content").at("ciphertext").get>(); +} + +mtx::crypto::OlmClient * +client(); + +void +handle_to_device_messages(const std::vector &msgs); + +void +handle_olm_message(const OlmMessage &msg); + +void +handle_olm_normal_message(const std::string &sender, + const std::string &sender_key, + const OlmCipherContent &content); + +void +handle_pre_key_olm_message(const std::string &sender, + const std::string &sender_key, + const OlmCipherContent &content); +} // namespace olm diff --git a/include/timeline/TimelineView.h b/include/timeline/TimelineView.h index 30af97fb..88857222 100644 --- a/include/timeline/TimelineView.h +++ b/include/timeline/TimelineView.h @@ -149,6 +149,9 @@ private: QWidget *relativeWidget(TimelineItem *item, int dt) const; + TimelineEvent parseEncryptedEvent( + const mtx::events::EncryptedEvent &e); + //! Callback for all message sending. void sendRoomMessageHandler(const std::string &txn_id, const mtx::responses::EventId &res, diff --git a/src/Cache.cc b/src/Cache.cc index 2a555425..150990b7 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -31,25 +31,42 @@ //! Should be changed when a breaking change occurs in the cache format. //! This will reset client's data. -static const std::string CURRENT_CACHE_FORMAT_VERSION("2018.05.11"); +static const std::string CURRENT_CACHE_FORMAT_VERSION("2018.06.10"); +static const std::string SECRET("secret"); static const lmdb::val NEXT_BATCH_KEY("next_batch"); +static const lmdb::val OLM_ACCOUNT_KEY("olm_account"); static const lmdb::val CACHE_FORMAT_VERSION_KEY("cache_format_version"); //! Cache databases and their format. //! //! Contains UI information for the joined rooms. (i.e name, topic, avatar url etc). //! Format: room_id -> RoomInfo -static constexpr const char *ROOMS_DB = "rooms"; -static constexpr const char *INVITES_DB = "invites"; +constexpr auto ROOMS_DB("rooms"); +constexpr auto INVITES_DB("invites"); //! Keeps already downloaded media for reuse. //! Format: matrix_url -> binary data. -static constexpr const char *MEDIA_DB = "media"; +constexpr auto MEDIA_DB("media"); //! Information that must be kept between sync requests. -static constexpr const char *SYNC_STATE_DB = "sync_state"; +constexpr auto SYNC_STATE_DB("sync_state"); //! Read receipts per room/event. -static constexpr const char *READ_RECEIPTS_DB = "read_receipts"; -static constexpr const char *NOTIFICATIONS_DB = "sent_notifications"; +constexpr auto READ_RECEIPTS_DB("read_receipts"); +constexpr auto NOTIFICATIONS_DB("sent_notifications"); + +//! Encryption related databases. + +//! user_id -> list of devices +constexpr auto DEVICES_DB("devices"); +//! device_id -> device keys +constexpr auto DEVICE_KEYS_DB("device_keys"); +//! room_ids that have encryption enabled. +// constexpr auto ENCRYPTED_ROOMS_DB("encrypted_rooms"); + +//! MegolmSessionIndex -> pickled OlmInboundGroupSession +constexpr auto INBOUND_MEGOLM_SESSIONS_DB("inbound_megolm_sessions"); +//! MegolmSessionIndex -> pickled OlmOutboundGroupSession +constexpr auto OUTBOUND_MEGOLM_SESSIONS_DB("outbound_megolm_sessions"); +constexpr auto OUTBOUND_OLM_SESSIONS_DB("outbound_olm_sessions"); using CachedReceipts = std::multimap>; using Receipts = std::map>; @@ -79,7 +96,7 @@ client() { return instance_.get(); } -} +} // namespace cache Cache::Cache(const QString &userId, QObject *parent) : QObject{parent} @@ -90,6 +107,11 @@ Cache::Cache(const QString &userId, QObject *parent) , mediaDb_{0} , readReceiptsDb_{0} , notificationsDb_{0} + , devicesDb_{0} + , deviceKeysDb_{0} + , inboundMegolmSessionDb_{0} + , outboundMegolmSessionDb_{0} + , outboundOlmSessionDb_{0} , localUserId_{userId} {} @@ -149,9 +171,221 @@ Cache::setup() mediaDb_ = lmdb::dbi::open(txn, MEDIA_DB, MDB_CREATE); readReceiptsDb_ = lmdb::dbi::open(txn, READ_RECEIPTS_DB, MDB_CREATE); notificationsDb_ = lmdb::dbi::open(txn, NOTIFICATIONS_DB, MDB_CREATE); + + // Device management + devicesDb_ = lmdb::dbi::open(txn, DEVICES_DB, MDB_CREATE); + deviceKeysDb_ = lmdb::dbi::open(txn, DEVICE_KEYS_DB, MDB_CREATE); + + // Session management + inboundMegolmSessionDb_ = lmdb::dbi::open(txn, INBOUND_MEGOLM_SESSIONS_DB, MDB_CREATE); + outboundMegolmSessionDb_ = lmdb::dbi::open(txn, OUTBOUND_MEGOLM_SESSIONS_DB, MDB_CREATE); + outboundOlmSessionDb_ = lmdb::dbi::open(txn, OUTBOUND_OLM_SESSIONS_DB, MDB_CREATE); + + txn.commit(); +} + +// +// Device Management +// + +// +// Session Management +// + +void +Cache::saveInboundMegolmSession(const MegolmSessionIndex &index, + mtx::crypto::InboundGroupSessionPtr session) +{ + using namespace mtx::crypto; + const auto key = index.to_hash(); + const auto pickled = pickle(session.get(), SECRET); + + auto txn = lmdb::txn::begin(env_); + lmdb::dbi_put(txn, inboundMegolmSessionDb_, lmdb::val(key), lmdb::val(pickled)); + txn.commit(); + + { + std::unique_lock lock(session_storage.group_inbound_mtx); + session_storage.group_inbound_sessions[key] = std::move(session); + } +} + +OlmInboundGroupSession * +Cache::getInboundMegolmSession(const MegolmSessionIndex &index) +{ + std::unique_lock lock(session_storage.group_inbound_mtx); + return session_storage.group_inbound_sessions[index.to_hash()].get(); +} + +bool +Cache::inboundMegolmSessionExists(const MegolmSessionIndex &index) noexcept +{ + std::unique_lock lock(session_storage.group_inbound_mtx); + return session_storage.group_inbound_sessions.find(index.to_hash()) != + session_storage.group_inbound_sessions.end(); +} + +void +Cache::saveOutboundMegolmSession(const MegolmSessionIndex &index, + const OutboundGroupSessionData &data, + mtx::crypto::OutboundGroupSessionPtr session) +{ + using namespace mtx::crypto; + const auto key = index.to_hash(); + const auto pickled = pickle(session.get(), SECRET); + + json j; + j["data"] = data; + j["session"] = pickled; + + auto txn = lmdb::txn::begin(env_); + lmdb::dbi_put(txn, outboundMegolmSessionDb_, lmdb::val(key), lmdb::val(j.dump())); + txn.commit(); + + { + std::unique_lock lock(session_storage.group_outbound_mtx); + session_storage.group_outbound_session_data[key] = data; + session_storage.group_outbound_sessions[key] = std::move(session); + } +} + +bool +Cache::outboundMegolmSessionExists(const MegolmSessionIndex &index) noexcept +{ + const auto key = index.to_hash(); + + std::unique_lock lock(session_storage.group_outbound_mtx); + return (session_storage.group_outbound_sessions.find(key) != + session_storage.group_outbound_sessions.end()) && + (session_storage.group_outbound_session_data.find(key) != + session_storage.group_outbound_session_data.end()); +} + +OutboundGroupSessionDataRef +Cache::getOutboundMegolmSession(const MegolmSessionIndex &index) +{ + const auto key = index.to_hash(); + std::unique_lock lock(session_storage.group_outbound_mtx); + return OutboundGroupSessionDataRef{session_storage.group_outbound_sessions[key].get(), + session_storage.group_outbound_session_data[key]}; +} + +void +Cache::saveOutboundOlmSession(const std::string &curve25519, mtx::crypto::OlmSessionPtr session) +{ + using namespace mtx::crypto; + const auto pickled = pickle(session.get(), SECRET); + + auto txn = lmdb::txn::begin(env_); + lmdb::dbi_put(txn, outboundMegolmSessionDb_, lmdb::val(curve25519), lmdb::val(pickled)); txn.commit(); + + { + std::unique_lock lock(session_storage.outbound_mtx); + session_storage.outbound_sessions[curve25519] = std::move(session); + } +} + +bool +Cache::outboundOlmSessionsExists(const std::string &curve25519) noexcept +{ + std::unique_lock lock(session_storage.outbound_mtx); + return session_storage.outbound_sessions.find(curve25519) != + session_storage.outbound_sessions.end(); } +OlmSession * +Cache::getOutboundOlmSession(const std::string &curve25519) +{ + std::unique_lock lock(session_storage.outbound_mtx); + return session_storage.outbound_sessions.at(curve25519).get(); +} + +void +Cache::saveOlmAccount(const std::string &data) +{ + auto txn = lmdb::txn::begin(env_); + lmdb::dbi_put(txn, syncStateDb_, OLM_ACCOUNT_KEY, lmdb::val(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(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(); + + auto session = + unpickle(obj.at("session"), SECRET); + session_storage.group_outbound_sessions[key] = std::move(session); + } catch (const nlohmann::json::exception &e) { + log::db()->warn("failed to parse outbound megolm session data: {}", + e.what()); + } + } + cursor.close(); + } + + // + // Outbound Olm Sessions + // + { + auto cursor = lmdb::cursor::open(txn, outboundOlmSessionDb_); + while (cursor.get(key, value, MDB_NEXT)) { + auto session = unpickle(value, SECRET); + session_storage.outbound_sessions[key] = std::move(session); + } + cursor.close(); + } + + txn.commit(); + + log::db()->info("sessions restored"); +} + +std::string +Cache::restoreOlmAccount() +{ + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + lmdb::val pickled; + lmdb::dbi_get(txn, syncStateDb_, OLM_ACCOUNT_KEY, pickled); + txn.commit(); + + return std::string(pickled.data(), pickled.size()); +} + +// +// Media Management +// + void Cache::saveImage(const std::string &url, const std::string &img_data) { diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 64ce69d6..a5a6a8c0 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -25,6 +25,7 @@ #include "Logging.hpp" #include "MainWindow.h" #include "MatrixClient.h" +#include "Olm.hpp" #include "OverlayModal.h" #include "QuickSwitcher.h" #include "RoomList.h" @@ -43,8 +44,12 @@ #include "dialogs/ReadReceipts.h" #include "timeline/TimelineViewManager.h" +// TODO: Needs to be updated with an actual secret. +static const std::string STORAGE_SECRET_KEY("secret"); + ChatPage *ChatPage::instance_ = nullptr; constexpr int CHECK_CONNECTIVITY_INTERVAL = 15'000; +constexpr size_t MAX_ONETIME_KEYS = 50; ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) : QWidget(parent) @@ -612,6 +617,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync); connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync); + connect(this, &ChatPage::tryDelayedSyncCb, this, [this]() { + QTimer::singleShot(5000, this, &ChatPage::trySync); + }); connect(this, &ChatPage::dropToLoginPageCb, this, &ChatPage::dropToLoginPage); @@ -728,6 +736,11 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) }); // TODO http::client()->getOwnCommunities(); + // The Olm client needs the user_id & device_id that will be included + // in the generated payloads & keys. + olm::client()->set_user_id(http::v2::client()->user_id().to_string()); + olm::client()->set_device_id(http::v2::client()->device_id()); + cache::init(userid); try { @@ -741,6 +754,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) if (cache::client()->isInitialized()) { loadStateFromCache(); + // TODO: Bootstrap olm client with saved data. return; } } catch (const lmdb::error &e) { @@ -749,6 +763,22 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) log::net()->info("falling back to initial sync"); } + try { + // It's the first time syncing with this device + // There isn't a saved olm account to restore. + log::crypto()->info("creating new olm account"); + olm::client()->create_new_account(); + cache::client()->saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); + } catch (const lmdb::error &e) { + log::crypto()->critical("failed to save olm account {}", e.what()); + emit dropToLoginPageCb(QString::fromStdString(e.what())); + return; + } catch (const mtx::crypto::olm_exception &e) { + log::crypto()->critical("failed to create new olm account {}", e.what()); + emit dropToLoginPageCb(QString::fromStdString(e.what())); + return; + } + tryInitialSync(); } @@ -826,16 +856,29 @@ ChatPage::loadStateFromCache() QtConcurrent::run([this]() { try { + cache::client()->restoreSessions(); + olm::client()->load(cache::client()->restoreOlmAccount(), + STORAGE_SECRET_KEY); + cache::client()->populateMembers(); emit initializeEmptyViews(cache::client()->joinedRooms()); emit initializeRoomList(cache::client()->roomInfo()); + } catch (const mtx::crypto::olm_exception &e) { + log::crypto()->critical("failed to restore olm account: {}", e.what()); + emit dropToLoginPageCb( + tr("Failed to restore OLM account. Please login again.")); + return; } catch (const lmdb::error &e) { - std::cout << "load cache error:" << e.what() << '\n'; - // TODO Clear cache and restart. + log::db()->critical("failed to restore cache: {}", e.what()); + emit dropToLoginPageCb( + tr("Failed to restore save data. Please login again.")); return; } + log::crypto()->info("ed25519 : {}", olm::client()->identity_keys().ed25519); + log::crypto()->info("curve25519: {}", olm::client()->identity_keys().curve25519); + // Start receiving events. emit trySyncCb(); @@ -1008,49 +1051,40 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) void ChatPage::tryInitialSync() { - mtx::http::SyncOpts opts; - opts.timeout = 0; + log::crypto()->info("ed25519 : {}", olm::client()->identity_keys().ed25519); + log::crypto()->info("curve25519: {}", olm::client()->identity_keys().curve25519); - log::net()->info("trying initial sync"); + // Upload one time keys for the device. + log::crypto()->info("generating one time keys"); + olm::client()->generate_one_time_keys(MAX_ONETIME_KEYS); - http::v2::client()->sync( - opts, [this](const mtx::responses::Sync &res, mtx::http::RequestErr err) { + http::v2::client()->upload_keys( + olm::client()->create_upload_keys_request(), + [this](const mtx::responses::UploadKeys &res, mtx::http::RequestErr err) { if (err) { - const auto error = QString::fromStdString(err->matrix_error.error); - const auto msg = tr("Please try to login again: %1").arg(error); - const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); const int status_code = static_cast(err->status_code); - - log::net()->error("sync error: {} {}", status_code, err_code); - - switch (status_code) { - case 502: - case 504: - case 524: { - emit tryInitialSyncCb(); - return; - } - default: { - emit dropToLoginPageCb(msg); - return; - } - } - } - - log::net()->info("initial sync completed"); - - try { - cache::client()->saveState(res); - emit initializeViews(std::move(res.rooms)); - emit initializeRoomList(cache::client()->roomInfo()); - } catch (const lmdb::error &e) { - log::db()->error("{}", e.what()); + log::crypto()->critical("failed to upload one time keys: {} {}", + err->matrix_error.error, + status_code); + // TODO We should have a timeout instead of keeping hammering the server. emit tryInitialSyncCb(); return; } - emit trySyncCb(); - emit contentLoaded(); + olm::client()->mark_keys_as_published(); + for (const auto &entry : res.one_time_key_counts) + log::net()->info( + "uploaded {} {} one-time keys", entry.second, entry.first); + + log::net()->info("trying initial sync"); + + mtx::http::SyncOpts opts; + opts.timeout = 0; + http::v2::client()->sync(opts, + std::bind(&ChatPage::initialSyncHandler, + this, + std::placeholders::_1, + std::placeholders::_2)); }); } @@ -1079,24 +1113,31 @@ ChatPage::trySync() log::net()->error("sync error: {} {}", status_code, err_code); + if (status_code <= 0 || status_code >= 600) { + if (!http::v2::is_logged_in()) + return; + + emit dropToLoginPageCb(msg); + return; + } + switch (status_code) { case 502: case 504: case 524: { - emit trySync(); + emit trySyncCb(); return; } case 401: case 403: { - // We are logged out. - if (http::v2::client()->access_token().empty()) + if (!http::v2::is_logged_in()) return; emit dropToLoginPageCb(msg); return; } default: { - emit trySync(); + emit tryDelayedSyncCb(); return; } } @@ -1104,9 +1145,14 @@ ChatPage::trySync() log::net()->debug("sync completed: {}", res.next_batch); + // Ensure that we have enough one-time keys available. + ensureOneTimeKeyCount(res.device_one_time_keys_count); + // TODO: fine grained error handling try { cache::client()->saveState(res); + olm::handle_to_device_messages(res.to_device); + emit syncUI(res.rooms); auto updates = cache::client()->roomUpdates(res); @@ -1194,3 +1240,74 @@ ChatPage::sendTypingNotifications() } }); } + +void +ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err) +{ + if (err) { + const auto error = QString::fromStdString(err->matrix_error.error); + const auto msg = tr("Please try to login again: %1").arg(error); + const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); + const int status_code = static_cast(err->status_code); + + log::net()->error("sync error: {} {}", status_code, err_code); + + switch (status_code) { + case 502: + case 504: + case 524: { + emit tryInitialSyncCb(); + return; + } + default: { + emit dropToLoginPageCb(msg); + return; + } + } + } + + log::net()->info("initial sync completed"); + + try { + cache::client()->saveState(res); + + olm::handle_to_device_messages(res.to_device); + + emit initializeViews(std::move(res.rooms)); + emit initializeRoomList(cache::client()->roomInfo()); + } catch (const lmdb::error &e) { + log::db()->error("{}", e.what()); + emit tryInitialSyncCb(); + return; + } + + emit trySyncCb(); + emit contentLoaded(); +} + +void +ChatPage::ensureOneTimeKeyCount(const std::map &counts) +{ + for (const auto &entry : counts) { + if (entry.second < MAX_ONETIME_KEYS) { + const int nkeys = MAX_ONETIME_KEYS - entry.second; + + log::crypto()->info("uploading {} {} keys", nkeys, entry.first); + olm::client()->generate_one_time_keys(nkeys); + + http::v2::client()->upload_keys( + olm::client()->create_upload_keys_request(), + [](const mtx::responses::UploadKeys &, mtx::http::RequestErr err) { + if (err) { + log::crypto()->warn( + "failed to update one-time keys: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + return; + } + + olm::client()->mark_keys_as_published(); + }); + } + } +} diff --git a/src/CommunitiesList.cc b/src/CommunitiesList.cc index 8ccd5e9d..49affcb7 100644 --- a/src/CommunitiesList.cc +++ b/src/CommunitiesList.cc @@ -128,6 +128,9 @@ CommunitiesList::fetchCommunityAvatar(const QString &id, const QString &avatarUr return; } + if (avatarUrl.isEmpty()) + return; + mtx::http::ThumbOpts opts; opts.mxc_url = avatarUrl.toStdString(); http::v2::client()->get_thumbnail( diff --git a/src/Logging.cpp b/src/Logging.cpp index c6c1c502..77e61e09 100644 --- a/src/Logging.cpp +++ b/src/Logging.cpp @@ -4,9 +4,10 @@ #include namespace { -std::shared_ptr db_logger = nullptr; -std::shared_ptr net_logger = nullptr; -std::shared_ptr main_logger = nullptr; +std::shared_ptr db_logger = nullptr; +std::shared_ptr net_logger = nullptr; +std::shared_ptr crypto_logger = nullptr; +std::shared_ptr main_logger = nullptr; constexpr auto MAX_FILE_SIZE = 1024 * 1024 * 6; constexpr auto MAX_LOG_FILES = 3; @@ -28,6 +29,8 @@ init(const std::string &file_path) net_logger = std::make_shared("net", std::begin(sinks), std::end(sinks)); main_logger = std::make_shared("main", std::begin(sinks), std::end(sinks)); db_logger = std::make_shared("db", std::begin(sinks), std::end(sinks)); + crypto_logger = + std::make_shared("crypto", std::begin(sinks), std::end(sinks)); } std::shared_ptr @@ -47,4 +50,10 @@ db() { return db_logger; } + +std::shared_ptr +crypto() +{ + return crypto_logger; +} } diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 9ba8b28e..cca51f03 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -46,15 +46,6 @@ MainWindow *MainWindow::instance_ = nullptr; -MainWindow::~MainWindow() -{ - if (http::v2::client() != nullptr) { - http::v2::client()->shutdown(); - // TODO: find out why waiting for the threads to join is slow. - http::v2::client()->close(); - } -} - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , progressModal_{nullptr} @@ -154,9 +145,11 @@ MainWindow::MainWindow(QWidget *parent) QString token = settings.value("auth/access_token").toString(); QString home_server = settings.value("auth/home_server").toString(); QString user_id = settings.value("auth/user_id").toString(); + QString device_id = settings.value("auth/device_id").toString(); http::v2::client()->set_access_token(token.toStdString()); http::v2::client()->set_server(home_server.toStdString()); + http::v2::client()->set_device_id(device_id.toStdString()); try { using namespace mtx::identifiers; @@ -228,6 +221,7 @@ void MainWindow::showChatPage() { auto userid = QString::fromStdString(http::v2::client()->user_id().to_string()); + auto device_id = QString::fromStdString(http::v2::client()->device_id()); auto homeserver = QString::fromStdString(http::v2::client()->server() + ":" + std::to_string(http::v2::client()->port())); auto token = QString::fromStdString(http::v2::client()->access_token()); @@ -236,6 +230,7 @@ MainWindow::showChatPage() settings.setValue("auth/access_token", token); settings.setValue("auth/home_server", homeserver); settings.setValue("auth/user_id", userid); + settings.setValue("auth/device_id", device_id); showOverlayProgressBar(); diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index 0eb4658a..d4ab8e33 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -3,7 +3,7 @@ #include namespace { -auto v2_client_ = std::make_shared("matrix.org"); +auto v2_client_ = std::make_shared(); } namespace http { @@ -15,6 +15,12 @@ client() return v2_client_.get(); } +bool +is_logged_in() +{ + return !v2_client_->access_token().empty(); +} + } // namespace v2 void diff --git a/src/Olm.cpp b/src/Olm.cpp new file mode 100644 index 00000000..769b0234 --- /dev/null +++ b/src/Olm.cpp @@ -0,0 +1,139 @@ +#include "Olm.hpp" + +#include "Cache.h" +#include "Logging.hpp" + +using namespace mtx::crypto; + +namespace { +auto client_ = std::make_unique(); +} + +namespace olm { + +mtx::crypto::OlmClient * +client() +{ + return client_.get(); +} + +void +handle_to_device_messages(const std::vector &msgs) +{ + if (msgs.empty()) + return; + + log::crypto()->info("received {} to_device messages", msgs.size()); + + for (const auto &msg : msgs) { + try { + OlmMessage olm_msg = msg; + handle_olm_message(std::move(olm_msg)); + } catch (const nlohmann::json::exception &e) { + log::crypto()->warn( + "parsing error for olm message: {} {}", e.what(), msg.dump(2)); + } catch (const std::invalid_argument &e) { + log::crypto()->warn( + "validation error for olm message: {} {}", e.what(), msg.dump(2)); + } + } +} + +void +handle_olm_message(const OlmMessage &msg) +{ + log::crypto()->info("sender : {}", msg.sender); + log::crypto()->info("sender_key: {}", msg.sender_key); + + const auto my_key = olm::client()->identity_keys().curve25519; + + for (const auto &cipher : msg.ciphertext) { + // We skip messages not meant for the current device. + if (cipher.first != my_key) + continue; + + const auto type = cipher.second.type; + log::crypto()->info("type: {}", type == 0 ? "OLM_PRE_KEY" : "OLM_MESSAGE"); + + if (type == OLM_MESSAGE_TYPE_PRE_KEY) + handle_pre_key_olm_message(msg.sender, msg.sender_key, cipher.second); + else + handle_olm_normal_message(msg.sender, msg.sender_key, cipher.second); + } +} + +void +handle_pre_key_olm_message(const std::string &sender, + const std::string &sender_key, + const OlmCipherContent &content) +{ + log::crypto()->info("opening olm session with {}", sender); + + OlmSessionPtr inbound_session = nullptr; + try { + inbound_session = olm::client()->create_inbound_session(content.body); + } catch (const olm_exception &e) { + log::crypto()->critical( + "failed to create inbound session with {}: {}", sender, e.what()); + return; + } + + if (!matches_inbound_session_from(inbound_session.get(), sender_key, content.body)) { + log::crypto()->warn("inbound olm session doesn't match sender's key ({})", sender); + return; + } + + mtx::crypto::BinaryBuf output; + try { + output = olm::client()->decrypt_message( + inbound_session.get(), OLM_MESSAGE_TYPE_PRE_KEY, content.body); + } catch (const olm_exception &e) { + log::crypto()->critical( + "failed to decrypt olm message {}: {}", content.body, e.what()); + return; + } + + auto plaintext = json::parse(std::string((char *)output.data(), output.size())); + log::crypto()->info("decrypted message: \n {}", plaintext.dump(2)); + + std::string room_id, session_id, session_key; + try { + room_id = plaintext.at("content").at("room_id"); + session_id = plaintext.at("content").at("session_id"); + session_key = plaintext.at("content").at("session_key"); + } catch (const nlohmann::json::exception &e) { + log::crypto()->critical( + "failed to parse plaintext olm message: {} {}", e.what(), plaintext.dump(2)); + return; + } + + MegolmSessionIndex index; + index.room_id = room_id; + index.session_id = session_id; + index.sender_key = sender_key; + + if (!cache::client()->inboundMegolmSessionExists(index)) { + auto megolm_session = olm::client()->init_inbound_group_session(session_key); + + try { + cache::client()->saveInboundMegolmSession(index, std::move(megolm_session)); + } catch (const lmdb::error &e) { + log::crypto()->critical("failed to save inbound megolm session: {}", + e.what()); + return; + } + + log::crypto()->info("established inbound megolm session ({}, {})", room_id, sender); + } else { + log::crypto()->warn( + "inbound megolm session already exists ({}, {})", room_id, sender); + } +} + +void +handle_olm_normal_message(const std::string &, const std::string &, const OlmCipherContent &) +{ + log::crypto()->warn("olm(1) not implemeted yet"); +} + +} // namespace olm diff --git a/src/RoomList.cc b/src/RoomList.cc index d3ed2e66..4891f746 100644 --- a/src/RoomList.cc +++ b/src/RoomList.cc @@ -96,7 +96,7 @@ RoomList::updateAvatar(const QString &room_id, const QString &url) opts, [room_id, opts, this](const std::string &res, mtx::http::RequestErr err) { if (err) { log::net()->warn( - "failed to download thumbnail: {}, {} - {}", + "failed to download room avatar: {} {} {}", opts.mxc_url, mtx::errors::to_string(err->matrix_error.errcode), err->matrix_error.error); diff --git a/src/main.cc b/src/main.cc index 1df8d0c9..13a712f4 100644 --- a/src/main.cc +++ b/src/main.cc @@ -149,7 +149,13 @@ main(int argc, char *argv[]) !settings.value("user/window/tray", true).toBool()) w.show(); - QObject::connect(&app, &QApplication::aboutToQuit, &w, &MainWindow::saveCurrentWindowSize); + QObject::connect(&app, &QApplication::aboutToQuit, &w, [&w]() { + w.saveCurrentWindowSize(); + if (http::v2::client() != nullptr) { + http::v2::client()->shutdown(); + http::v2::client()->close(); + } + }); log::main()->info("starting nheko {}", nheko::version); diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc index 5ef390a9..9baa1f4a 100644 --- a/src/timeline/TimelineView.cc +++ b/src/timeline/TimelineView.cc @@ -24,6 +24,7 @@ #include "Config.h" #include "FloatingButton.h" #include "Logging.hpp" +#include "Olm.hpp" #include "UserSettingsPage.h" #include "Utils.h" @@ -235,19 +236,19 @@ TimelineItem * TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents &event, TimelineDirection direction) { - namespace msg = mtx::events::msg; - using AudioEvent = mtx::events::RoomEvent; - using EmoteEvent = mtx::events::RoomEvent; - using FileEvent = mtx::events::RoomEvent; - using ImageEvent = mtx::events::RoomEvent; - using NoticeEvent = mtx::events::RoomEvent; - using TextEvent = mtx::events::RoomEvent; - using VideoEvent = mtx::events::RoomEvent; + using namespace mtx::events; + + using AudioEvent = RoomEvent; + using EmoteEvent = RoomEvent; + using FileEvent = RoomEvent; + using ImageEvent = RoomEvent; + using NoticeEvent = RoomEvent; + using TextEvent = RoomEvent; + using VideoEvent = RoomEvent; - if (mpark::holds_alternative>(event)) { - auto redaction_event = - mpark::get>(event); - const auto event_id = QString::fromStdString(redaction_event.redacts); + if (mpark::holds_alternative>(event)) { + auto redaction_event = mpark::get>(event); + const auto event_id = QString::fromStdString(redaction_event.redacts); QTimer::singleShot(0, this, [event_id, this]() { if (eventIds_.contains(event_id)) @@ -255,35 +256,88 @@ TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents & }); return nullptr; - } else if (mpark::holds_alternative>(event)) { - auto audio = mpark::get>(event); + } else if (mpark::holds_alternative>(event)) { + auto audio = mpark::get>(event); return processMessageEvent(audio, direction); - } else if (mpark::holds_alternative>(event)) { - auto emote = mpark::get>(event); + } else if (mpark::holds_alternative>(event)) { + auto emote = mpark::get>(event); return processMessageEvent(emote, direction); - } else if (mpark::holds_alternative>(event)) { - auto file = mpark::get>(event); + } else if (mpark::holds_alternative>(event)) { + auto file = mpark::get>(event); return processMessageEvent(file, direction); - } else if (mpark::holds_alternative>(event)) { - auto image = mpark::get>(event); + } else if (mpark::holds_alternative>(event)) { + auto image = mpark::get>(event); return processMessageEvent(image, direction); - } else if (mpark::holds_alternative>(event)) { - auto notice = mpark::get>(event); + } else if (mpark::holds_alternative>(event)) { + auto notice = mpark::get>(event); return processMessageEvent(notice, direction); - } else if (mpark::holds_alternative>(event)) { - auto text = mpark::get>(event); + } else if (mpark::holds_alternative>(event)) { + auto text = mpark::get>(event); return processMessageEvent(text, direction); - } else if (mpark::holds_alternative>(event)) { - auto video = mpark::get>(event); + } else if (mpark::holds_alternative>(event)) { + auto video = mpark::get>(event); return processMessageEvent(video, direction); - } else if (mpark::holds_alternative(event)) { - return processMessageEvent( - mpark::get(event), direction); + } else if (mpark::holds_alternative(event)) { + return processMessageEvent(mpark::get(event), + direction); + } else if (mpark::holds_alternative>(event)) { + auto decrypted = + parseEncryptedEvent(mpark::get>(event)); + return parseMessageEvent(decrypted, direction); } return nullptr; } +TimelineEvent +TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEvent &e) +{ + MegolmSessionIndex index; + index.room_id = room_id_.toStdString(); + index.session_id = e.content.session_id; + index.sender_key = e.content.sender_key; + + mtx::events::RoomEvent dummy; + dummy.origin_server_ts = e.origin_server_ts; + dummy.event_id = e.event_id; + dummy.sender = e.sender; + dummy.content.body = "-- Encrypted Event (No keys found for decryption) --"; + + if (!cache::client()->inboundMegolmSessionExists(index)) { + log::crypto()->info("Could not find inbound megolm session ({}, {}, {})", + index.room_id, + index.session_id, + e.sender); + // TODO: request megolm session_id & session_key from the sender. + return dummy; + } + + auto session = cache::client()->getInboundMegolmSession(index); + auto res = olm::client()->decrypt_group_message(session, e.content.ciphertext); + + const auto msg_str = std::string((char *)res.data.data(), res.data.size()); + + // Add missing fields for the event. + json body = json::parse(msg_str); + body["event_id"] = e.event_id; + body["sender"] = e.sender; + body["origin_server_ts"] = e.origin_server_ts; + + log::crypto()->info("decrypted data: \n {}", body.dump(2)); + + json event_array = json::array(); + event_array.push_back(body); + + std::vector events; + mtx::responses::utils::parse_timeline_events(event_array, events); + + if (events.size() == 1) + return events.at(0); + + dummy.content.body = "-- Encrypted Event (Unknown event type) --"; + return dummy; +} + void TimelineView::renderBottomEvents(const std::vector &events) { -- cgit 1.5.1 From 8e9d210a2a2136d1d517645b2b2412b15da09360 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 14 Jun 2018 02:28:35 +0300 Subject: Rename the log namespace to avoid symbol clash with the math function - Patch the olm repo with a CMakeLists.txt file --- deps/CMakeLists.txt | 2 +- deps/cmake/Olm.cmake | 26 +++---- deps/cmake/OlmCMakeLists.txt | 107 ++++++++++++++++++++++++++++ deps/cmake/OlmConfig.cmake.in | 11 +++ include/Logging.hpp | 4 +- include/timeline/TimelineItem.h | 14 +--- src/AvatarProvider.cc | 8 +-- src/Cache.cc | 86 ++++++++++++----------- src/ChatPage.cc | 135 ++++++++++++++++++------------------ src/CommunitiesList.cc | 2 +- src/Logging.cpp | 14 ++-- src/MainWindow.cc | 2 +- src/Olm.cpp | 38 +++++----- src/RegisterPage.cc | 6 +- src/RoomList.cc | 12 ++-- src/dialogs/PreviewUploadOverlay.cc | 9 +-- src/dialogs/RoomSettings.cpp | 15 ++-- src/main.cc | 8 +-- src/timeline/TimelineItem.cc | 17 +++++ src/timeline/TimelineView.cc | 88 +++++++++++------------ src/timeline/TimelineViewManager.cc | 16 +++-- src/timeline/widgets/AudioItem.cc | 9 +-- src/timeline/widgets/FileItem.cc | 11 +-- src/timeline/widgets/ImageItem.cc | 32 +++++---- src/timeline/widgets/VideoItem.cc | 1 - 25 files changed, 408 insertions(+), 265 deletions(-) create mode 100644 deps/cmake/OlmCMakeLists.txt create mode 100644 deps/cmake/OlmConfig.cmake.in (limited to 'src/Logging.cpp') diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 5f9b48ca..c948a097 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -40,7 +40,7 @@ set(MATRIX_STRUCTS_URL https://github.com/mujx/matrix-structs) set(MATRIX_STRUCTS_TAG eeb7373729a1618e2b3838407863342b88b8a0de) set(MTXCLIENT_URL https://github.com/mujx/mtxclient) -set(MTXCLIENT_TAG 26aad7088b9532808ded9919d55f58711c0138e3) +set(MTXCLIENT_TAG 688d5b0fd1fd16319d7fcbdbf938109eaa850545) set(OLM_URL https://git.matrix.org/git/olm.git) set(OLM_TAG 4065c8e11a33ba41133a086ed3de4da94dcb6bae) diff --git a/deps/cmake/Olm.cmake b/deps/cmake/Olm.cmake index dde18db9..c476f71d 100644 --- a/deps/cmake/Olm.cmake +++ b/deps/cmake/Olm.cmake @@ -1,8 +1,4 @@ -if(MSVC) - set(MAKE_CMD "mingw32-make.exe") -else() - set(MAKE_CMD "make") -endif() +set(OLM_PATCH ${CMAKE_CURRENT_SOURCE_DIR}/patches/olm-CMake-Support.patch) ExternalProject_Add( Olm @@ -12,12 +8,18 @@ ExternalProject_Add( BUILD_IN_SOURCE 1 SOURCE_DIR ${DEPS_BUILD_DIR}/olm - CONFIGURE_COMMAND "" - BUILD_COMMAND ${MAKE_CMD} static - INSTALL_COMMAND - mkdir -p ${DEPS_INSTALL_DIR}/lib && - cp -R ${DEPS_BUILD_DIR}/olm/include ${DEPS_INSTALL_DIR} && - cp ${DEPS_BUILD_DIR}/olm/build/libolm.a ${DEPS_INSTALL_DIR}/lib -) + CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/OlmCMakeLists.txt + ${DEPS_BUILD_DIR}/olm/CMakeLists.txt + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/OlmConfig.cmake.in + ${DEPS_BUILD_DIR}/olm/cmake/OlmConfig.cmake.in + COMMAND ${CMAKE_COMMAND} + -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} + -DCMAKE_BUILD_TYPE=Release + ${DEPS_BUILD_DIR}/olm + BUILD_COMMAND ${CMAKE_COMMAND} + --build ${DEPS_BUILD_DIR}/olm + --config Release) list(APPEND THIRD_PARTY_DEPS Olm) diff --git a/deps/cmake/OlmCMakeLists.txt b/deps/cmake/OlmCMakeLists.txt new file mode 100644 index 00000000..529cbb92 --- /dev/null +++ b/deps/cmake/OlmCMakeLists.txt @@ -0,0 +1,107 @@ +cmake_minimum_required(VERSION 3.1) + +project(olm VERSION 2.2.2 LANGUAGES CXX C) + +add_definitions(-DOLMLIB_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}) +add_definitions(-DOLMLIB_VERSION_MINOR=${PROJECT_VERSION_MINOR}) +add_definitions(-DOLMLIB_VERSION_PATCH=${PROJECT_VERSION_PATCH}) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +add_library(olm + src/account.cpp + src/base64.cpp + src/cipher.cpp + src/crypto.cpp + src/memory.cpp + src/message.cpp + src/pickle.cpp + src/ratchet.cpp + src/session.cpp + src/utility.cpp + + src/ed25519.c + src/error.c + src/inbound_group_session.c + src/megolm.c + src/olm.cpp + src/outbound_group_session.c + src/pickle_encoding.c + + lib/crypto-algorithms/aes.c + lib/crypto-algorithms/sha256.c + lib/curve25519-donna/curve25519-donna.c) +add_library(Olm::Olm ALIAS olm) + +target_include_directories(olm + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/lib) + +set_target_properties(olm PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION}) + +set_target_properties(olm PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR} + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) + +# +# Installation +# +include(GNUInstallDirs) +set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Olm) +install(TARGETS olm + EXPORT olm-targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +# The exported target will be named Olm. +set_target_properties(olm PROPERTIES EXPORT_NAME Olm) +install(FILES + ${CMAKE_SOURCE_DIR}/include/olm/olm.h + ${CMAKE_SOURCE_DIR}/include/olm/outbound_group_session.h + ${CMAKE_SOURCE_DIR}/include/olm/inbound_group_session.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/olm) + +# Export the targets to a script. +install(EXPORT olm-targets + FILE OlmTargets.cmake + NAMESPACE Olm:: + DESTINATION ${INSTALL_CONFIGDIR}) + +# Create a ConfigVersion.cmake file. +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/OlmConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) + +configure_package_config_file( + ${CMAKE_CURRENT_LIST_DIR}/cmake/OlmConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/OlmConfig.cmake + INSTALL_DESTINATION ${INSTALL_CONFIGDIR}) + +#Install the config & configversion. +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/OlmConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/OlmConfigVersion.cmake + DESTINATION ${INSTALL_CONFIGDIR}) + +# Register package in user's package registry +export(EXPORT olm-targets + FILE ${CMAKE_CURRENT_BINARY_DIR}/OlmTargets.cmake + NAMESPACE Olm::) +export(PACKAGE Olm) diff --git a/deps/cmake/OlmConfig.cmake.in b/deps/cmake/OlmConfig.cmake.in new file mode 100644 index 00000000..a7541f7a --- /dev/null +++ b/deps/cmake/OlmConfig.cmake.in @@ -0,0 +1,11 @@ +get_filename_component(Olm_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +include(CMakeFindDependencyMacro) + +list(APPEND CMAKE_MODULE_PATH ${Olm_CMAKE_DIR}) +list(REMOVE_AT CMAKE_MODULE_PATH -1) + +if(NOT TARGET Olm::olm) + include("${Olm_CMAKE_DIR}/OlmTargets.cmake") +endif() + +set(Olm_LIBRARIES Olm::olm) diff --git a/include/Logging.hpp b/include/Logging.hpp index bdbd3e2c..2feae60d 100644 --- a/include/Logging.hpp +++ b/include/Logging.hpp @@ -3,12 +3,12 @@ #include #include -namespace log { +namespace nhlog { void init(const std::string &file); std::shared_ptr -main(); +ui(); std::shared_ptr net(); diff --git a/include/timeline/TimelineItem.h b/include/timeline/TimelineItem.h index 4dcca1a5..6623a82c 100644 --- a/include/timeline/TimelineItem.h +++ b/include/timeline/TimelineItem.h @@ -194,19 +194,7 @@ public: void setEventId(const QString &event_id) { event_id_ = event_id; } void markReceived(); void setRoomId(QString room_id) { room_id_ = room_id; } - void sendReadReceipt() const - { - if (!event_id_.isEmpty()) - http::v2::client()->read_event( - room_id_.toStdString(), - event_id_.toStdString(), - [this](mtx::http::RequestErr err) { - if (err) { - qWarning() << QString("failed to read_event (%1, %2)") - .arg(room_id_, event_id_); - } - }); - } + void sendReadReceipt() const; //! Add a user avatar for this event. void addAvatar(); diff --git a/src/AvatarProvider.cc b/src/AvatarProvider.cc index ad095023..391f57d9 100644 --- a/src/AvatarProvider.cc +++ b/src/AvatarProvider.cc @@ -56,10 +56,10 @@ resolve(const QString &room_id, const QString &user_id, QObject *receiver, Avata opts, [opts, proxy = std::move(proxy)](const std::string &res, mtx::http::RequestErr err) { if (err) { - log::net()->warn("failed to download avatar: {} - ({} {})", - opts.mxc_url, - mtx::errors::to_string(err->matrix_error.errcode), - err->matrix_error.error); + nhlog::net()->warn("failed to download avatar: {} - ({} {})", + opts.mxc_url, + mtx::errors::to_string(err->matrix_error.errcode), + err->matrix_error.error); return; } diff --git a/src/Cache.cc b/src/Cache.cc index 7c678b72..20572ece 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -119,7 +119,7 @@ Cache::Cache(const QString &userId, QObject *parent) void Cache::setup() { - log::db()->debug("setting up cache"); + nhlog::db()->debug("setting up cache"); auto statePath = QString("%1/%2") .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) @@ -136,7 +136,7 @@ Cache::setup() env_.set_max_dbs(1024UL); if (isInitial) { - log::db()->info("initializing LMDB"); + nhlog::db()->info("initializing LMDB"); if (!QDir().mkpath(statePath)) { throw std::runtime_error( @@ -152,7 +152,7 @@ Cache::setup() std::string(e.what())); } - log::db()->warn("resetting cache due to LMDB version mismatch: {}", e.what()); + nhlog::db()->warn("resetting cache due to LMDB version mismatch: {}", e.what()); QDir stateDir(statePath); @@ -188,7 +188,7 @@ Cache::setup() void Cache::setEncryptedRoom(const std::string &room_id) { - log::db()->info("mark room {} as encrypted", room_id); + nhlog::db()->info("mark room {} as encrypted", room_id); auto txn = lmdb::txn::begin(env_); auto db = lmdb::dbi::open(txn, ENCRYPTED_ROOMS_DB, MDB_CREATE); @@ -398,7 +398,7 @@ Cache::restoreSessions() unpickle(obj.at("session"), SECRET); session_storage.group_outbound_sessions[key] = std::move(session); } catch (const nlohmann::json::exception &e) { - log::db()->critical( + nhlog::db()->critical( "failed to parse outbound megolm session data: {}", e.what()); } } @@ -419,7 +419,7 @@ Cache::restoreSessions() txn.commit(); - log::db()->info("sessions restored"); + nhlog::db()->info("sessions restored"); } std::string @@ -453,7 +453,7 @@ Cache::saveImage(const std::string &url, const std::string &img_data) txn.commit(); } catch (const lmdb::error &e) { - log::db()->critical("saveImage: {}", e.what()); + nhlog::db()->critical("saveImage: {}", e.what()); } } @@ -478,7 +478,7 @@ Cache::image(lmdb::txn &txn, const std::string &url) const return QByteArray(image.data(), image.size()); } catch (const lmdb::error &e) { - log::db()->critical("image: {}, {}", e.what(), url); + nhlog::db()->critical("image: {}, {}", e.what(), url); } return QByteArray(); @@ -506,7 +506,7 @@ Cache::image(const QString &url) const return QByteArray(image.data(), image.size()); } catch (const lmdb::error &e) { - log::db()->critical("image: {} {}", e.what(), url.toStdString()); + nhlog::db()->critical("image: {} {}", e.what(), url.toStdString()); } return QByteArray(); @@ -588,7 +588,7 @@ Cache::deleteData() // TODO: We need to remove the env_ while not accepting new requests. if (!cacheDirectory_.isEmpty()) { QDir(cacheDirectory_).removeRecursively(); - log::db()->info("deleted cache files from disk"); + nhlog::db()->info("deleted cache files from disk"); } } @@ -608,9 +608,9 @@ Cache::isFormatValid() std::string stored_version(current_version.data(), current_version.size()); if (stored_version != CURRENT_CACHE_FORMAT_VERSION) { - log::db()->warn("breaking changes in the cache format. stored: {}, current: {}", - stored_version, - CURRENT_CACHE_FORMAT_VERSION); + nhlog::db()->warn("breaking changes in the cache format. stored: {}, current: {}", + stored_version, + CURRENT_CACHE_FORMAT_VERSION); return false; } @@ -660,7 +660,7 @@ Cache::readReceipts(const QString &event_id, const QString &room_id) } } catch (const lmdb::error &e) { - log::db()->critical("readReceipts: {}", e.what()); + nhlog::db()->critical("readReceipts: {}", e.what()); } return receipts; @@ -710,7 +710,7 @@ Cache::updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Recei lmdb::val(merged_receipts.data(), merged_receipts.size())); } catch (const lmdb::error &e) { - log::db()->critical("updateReadReceipts: {}", e.what()); + nhlog::db()->critical("updateReadReceipts: {}", e.what()); } } } @@ -868,9 +868,9 @@ Cache::singleRoomInfo(const std::string &room_id) return tmp; } catch (const json::exception &e) { - log::db()->warn("failed to parse room info: room_id ({}), {}", - room_id, - std::string(data.data(), data.size())); + nhlog::db()->warn("failed to parse room info: room_id ({}), {}", + room_id, + std::string(data.data(), data.size())); } } @@ -900,9 +900,9 @@ Cache::getRoomInfo(const std::vector &rooms) room_info.emplace(QString::fromStdString(room), std::move(tmp)); } catch (const json::exception &e) { - log::db()->warn("failed to parse room info: room_id ({}), {}", - room, - std::string(data.data(), data.size())); + nhlog::db()->warn("failed to parse room info: room_id ({}), {}", + room, + std::string(data.data(), data.size())); } } else { // Check if the room is an invite. @@ -915,7 +915,7 @@ Cache::getRoomInfo(const std::vector &rooms) room_info.emplace(QString::fromStdString(room), std::move(tmp)); } catch (const json::exception &e) { - log::db()->warn( + nhlog::db()->warn( "failed to parse room info for invite: room_id ({}), {}", room, std::string(data.data(), data.size())); @@ -1003,7 +1003,7 @@ Cache::getRoomAvatarUrl(lmdb::txn &txn, return QString::fromStdString(msg.content.url); } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.avatar event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.avatar event: {}", e.what()); } } @@ -1026,7 +1026,7 @@ Cache::getRoomAvatarUrl(lmdb::txn &txn, cursor.close(); return QString::fromStdString(m.avatar_url); } catch (const json::exception &e) { - log::db()->warn("failed to parse member info: {}", e.what()); + nhlog::db()->warn("failed to parse member info: {}", e.what()); } } @@ -1053,7 +1053,7 @@ Cache::getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) if (!msg.content.name.empty()) return QString::fromStdString(msg.content.name); } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.name event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.name event: {}", e.what()); } } @@ -1068,8 +1068,8 @@ Cache::getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) if (!msg.content.alias.empty()) return QString::fromStdString(msg.content.alias); } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.canonical_alias event: {}", - e.what()); + nhlog::db()->warn("failed to parse m.room.canonical_alias event: {}", + e.what()); } } @@ -1085,7 +1085,7 @@ Cache::getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) try { members.emplace(user_id, json::parse(member_data)); } catch (const json::exception &e) { - log::db()->warn("failed to parse member info: {}", e.what()); + nhlog::db()->warn("failed to parse member info: {}", e.what()); } ii++; @@ -1129,7 +1129,7 @@ Cache::getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb) json::parse(std::string(event.data(), event.size())); return msg.content.join_rule; } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.join_rule event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.join_rule event: {}", e.what()); } } return JoinRule::Knock; @@ -1151,7 +1151,8 @@ Cache::getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb) json::parse(std::string(event.data(), event.size())); return msg.content.guest_access == AccessState::CanJoin; } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.guest_access event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.guest_access event: {}", + e.what()); } } return false; @@ -1175,7 +1176,7 @@ Cache::getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb) if (!msg.content.topic.empty()) return QString::fromStdString(msg.content.topic); } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.topic event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.topic event: {}", e.what()); } } @@ -1198,7 +1199,7 @@ Cache::getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &members json::parse(std::string(event.data(), event.size())); return QString::fromStdString(msg.content.name); } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.name event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.name event: {}", e.what()); } } @@ -1215,7 +1216,7 @@ Cache::getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &members return QString::fromStdString(tmp.name); } catch (const json::exception &e) { - log::db()->warn("failed to parse member info: {}", e.what()); + nhlog::db()->warn("failed to parse member info: {}", e.what()); } } @@ -1240,7 +1241,7 @@ Cache::getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &me json::parse(std::string(event.data(), event.size())); return QString::fromStdString(msg.content.url); } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.avatar event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.avatar event: {}", e.what()); } } @@ -1257,7 +1258,7 @@ Cache::getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &me return QString::fromStdString(tmp.avatar_url); } catch (const json::exception &e) { - log::db()->warn("failed to parse member info: {}", e.what()); + nhlog::db()->warn("failed to parse member info: {}", e.what()); } } @@ -1282,7 +1283,7 @@ Cache::getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &db) json::parse(std::string(event.data(), event.size())); return QString::fromStdString(msg.content.topic); } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.topic event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.topic event: {}", e.what()); } } @@ -1318,9 +1319,9 @@ Cache::getRoomAvatar(const std::string &room_id) return QImage(); } } catch (const json::exception &e) { - log::db()->warn("failed to parse room info: {}, {}", - e.what(), - std::string(response.data(), response.size())); + nhlog::db()->warn("failed to parse room info: {}, {}", + e.what(), + std::string(response.data(), response.size())); } if (!lmdb::dbi_get(txn, mediaDb_, lmdb::val(media_url), response)) { @@ -1356,7 +1357,7 @@ void Cache::populateMembers() { auto rooms = joinedRooms(); - log::db()->info("loading {} rooms", rooms.size()); + nhlog::db()->info("loading {} rooms", rooms.size()); auto txn = lmdb::txn::begin(env_); @@ -1484,7 +1485,7 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ QString::fromStdString(tmp.name), QImage::fromData(image(txn, tmp.avatar_url))}); } catch (const json::exception &e) { - log::db()->warn("{}", e.what()); + nhlog::db()->warn("{}", e.what()); } currentIndex += 1; @@ -1555,7 +1556,8 @@ Cache::hasEnoughPowerLevel(const std::vector &eventTypes std::min(min_event_level, (uint16_t)msg.content.state_level(to_string(ty))); } catch (const json::exception &e) { - log::db()->warn("failed to parse m.room.power_levels event: {}", e.what()); + nhlog::db()->warn("failed to parse m.room.power_levels event: {}", + e.what()); } } diff --git a/src/ChatPage.cc b/src/ChatPage.cc index c10d4aa2..cffb2a46 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -129,13 +129,13 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) typingRefresher_->setInterval(TYPING_REFRESH_TIMEOUT); connect(this, &ChatPage::connectionLost, this, [this]() { - log::net()->info("connectivity lost"); + nhlog::net()->info("connectivity lost"); isConnected_ = false; http::v2::client()->shutdown(); text_input_->disableInput(); }); connect(this, &ChatPage::connectionRestored, this, [this]() { - log::net()->info("trying to re-connect"); + nhlog::net()->info("trying to re-connect"); text_input_->enableInput(); isConnected_ = true; @@ -165,19 +165,20 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(this, &ChatPage::loggedOut, this, &ChatPage::logout); connect(user_info_widget_, &UserInfoWidget::logout, this, [this]() { - http::v2::client()->logout([this](const mtx::responses::Logout &, - mtx::http::RequestErr err) { - if (err) { - // TODO: handle special errors - emit contentLoaded(); - log::net()->warn("failed to logout: {} - {}", - mtx::errors::to_string(err->matrix_error.errcode), - err->matrix_error.error); - return; - } + http::v2::client()->logout( + [this](const mtx::responses::Logout &, mtx::http::RequestErr err) { + if (err) { + // TODO: handle special errors + emit contentLoaded(); + nhlog::net()->warn( + "failed to logout: {} - {}", + mtx::errors::to_string(err->matrix_error.errcode), + err->matrix_error.error); + return; + } - emit loggedOut(); - }); + emit loggedOut(); + }); emit showOverlayProgressBar(); }); @@ -252,8 +253,8 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) http::v2::client()->stop_typing( current_room_.toStdString(), [](mtx::http::RequestErr err) { if (err) { - log::net()->warn("failed to stop typing notifications: {}", - err->matrix_error.error); + nhlog::net()->warn("failed to stop typing notifications: {}", + err->matrix_error.error); } }); }); @@ -309,9 +310,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) if (err) { emit uploadFailed( tr("Failed to upload image. Please try again.")); - log::net()->warn("failed to upload image: {} ({})", - err->matrix_error.error, - static_cast(err->status_code)); + nhlog::net()->warn("failed to upload image: {} ({})", + err->matrix_error.error, + static_cast(err->status_code)); return; } @@ -352,9 +353,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) if (err) { emit uploadFailed( tr("Failed to upload file. Please try again.")); - log::net()->warn("failed to upload file: {} ({})", - err->matrix_error.error, - static_cast(err->status_code)); + nhlog::net()->warn("failed to upload file: {} ({})", + err->matrix_error.error, + static_cast(err->status_code)); return; } @@ -395,9 +396,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) if (err) { emit uploadFailed( tr("Failed to upload audio. Please try again.")); - log::net()->warn("failed to upload audio: {} ({})", - err->matrix_error.error, - static_cast(err->status_code)); + nhlog::net()->warn("failed to upload audio: {} ({})", + err->matrix_error.error, + static_cast(err->status_code)); return; } @@ -437,9 +438,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) if (err) { emit uploadFailed( tr("Failed to upload video. Please try again.")); - log::net()->warn("failed to upload video: {} ({})", - err->matrix_error.error, - static_cast(err->status_code)); + nhlog::net()->warn("failed to upload video: {} ({})", + err->matrix_error.error, + static_cast(err->status_code)); return; } @@ -569,7 +570,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) try { room_list_->cleanupInvites(cache::client()->invites()); } catch (const lmdb::error &e) { - log::db()->error("failed to retrieve invites: {}", e.what()); + nhlog::db()->error("failed to retrieve invites: {}", e.what()); } view_manager_->initialize(rooms); @@ -593,7 +594,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) [this](const mtx::responses::Notifications &res, mtx::http::RequestErr err) { if (err) { - log::net()->warn( + nhlog::net()->warn( "failed to retrieve notifications: {} ({})", err->matrix_error.error, static_cast(err->status_code)); @@ -690,7 +691,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) try { http::v2::client()->set_user(parse(userid.toStdString())); } catch (const std::invalid_argument &e) { - log::main()->critical("bootstrapped with invalid user_id: {}", + nhlog::ui()->critical("bootstrapped with invalid user_id: {}", userid.toStdString()); } @@ -709,7 +710,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) const bool isValid = cache::client()->isFormatValid(); if (isInitialized && !isValid) { - log::db()->warn("breaking changes in cache"); + nhlog::db()->warn("breaking changes in cache"); // TODO: Deleting session data but keep using the // same device doesn't work. cache::client()->deleteData(); @@ -721,23 +722,23 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) return; } } catch (const lmdb::error &e) { - log::db()->critical("failure during boot: {}", e.what()); + nhlog::db()->critical("failure during boot: {}", e.what()); cache::client()->deleteData(); - log::net()->info("falling back to initial sync"); + nhlog::net()->info("falling back to initial sync"); } try { // It's the first time syncing with this device // There isn't a saved olm account to restore. - log::crypto()->info("creating new olm account"); + nhlog::crypto()->info("creating new olm account"); olm::client()->create_new_account(); cache::client()->saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); } catch (const lmdb::error &e) { - log::crypto()->critical("failed to save olm account {}", e.what()); + nhlog::crypto()->critical("failed to save olm account {}", e.what()); emit dropToLoginPageCb(QString::fromStdString(e.what())); return; } catch (const mtx::crypto::olm_exception &e) { - log::crypto()->critical("failed to create new olm account {}", e.what()); + nhlog::crypto()->critical("failed to create new olm account {}", e.what()); emit dropToLoginPageCb(QString::fromStdString(e.what())); return; } @@ -771,7 +772,7 @@ void ChatPage::changeTopRoomInfo(const QString &room_id) { if (room_id.isEmpty()) { - log::main()->warn("cannot switch to empty room_id"); + nhlog::ui()->warn("cannot switch to empty room_id"); return; } @@ -795,7 +796,7 @@ ChatPage::changeTopRoomInfo(const QString &room_id) top_bar_->updateRoomAvatar(img); } catch (const lmdb::error &e) { - log::main()->error("failed to change top bar room info: {}", e.what()); + nhlog::ui()->error("failed to change top bar room info: {}", e.what()); } current_room_ = room_id; @@ -816,7 +817,7 @@ ChatPage::showUnreadMessageNotification(int count) void ChatPage::loadStateFromCache() { - log::db()->info("restoring state from cache"); + nhlog::db()->info("restoring state from cache"); getProfileInfo(); @@ -831,19 +832,19 @@ ChatPage::loadStateFromCache() emit initializeEmptyViews(cache::client()->joinedRooms()); emit initializeRoomList(cache::client()->roomInfo()); } catch (const mtx::crypto::olm_exception &e) { - log::crypto()->critical("failed to restore olm account: {}", e.what()); + nhlog::crypto()->critical("failed to restore olm account: {}", e.what()); emit dropToLoginPageCb( tr("Failed to restore OLM account. Please login again.")); return; } catch (const lmdb::error &e) { - log::db()->critical("failed to restore cache: {}", e.what()); + nhlog::db()->critical("failed to restore cache: {}", e.what()); emit dropToLoginPageCb( tr("Failed to restore save data. Please login again.")); return; } - log::crypto()->info("ed25519 : {}", olm::client()->identity_keys().ed25519); - log::crypto()->info("curve25519: {}", olm::client()->identity_keys().curve25519); + nhlog::crypto()->info("ed25519 : {}", olm::client()->identity_keys().ed25519); + nhlog::crypto()->info("curve25519: {}", olm::client()->identity_keys().curve25519); // Start receiving events. emit trySyncCb(); @@ -890,7 +891,7 @@ ChatPage::removeRoom(const QString &room_id) cache::client()->removeRoom(room_id); cache::client()->removeInvite(room_id.toStdString()); } catch (const lmdb::error &e) { - log::db()->critical("failure while removing room: {}", e.what()); + nhlog::db()->critical("failure while removing room: {}", e.what()); // TODO: Notify the user. } @@ -1009,7 +1010,7 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) utils::event_body(item.event)); } } catch (const lmdb::error &e) { - log::db()->warn("error while sending desktop notification: {}", e.what()); + nhlog::db()->warn("error while sending desktop notification: {}", e.what()); } } } @@ -1017,11 +1018,11 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) void ChatPage::tryInitialSync() { - log::crypto()->info("ed25519 : {}", olm::client()->identity_keys().ed25519); - log::crypto()->info("curve25519: {}", olm::client()->identity_keys().curve25519); + nhlog::crypto()->info("ed25519 : {}", olm::client()->identity_keys().ed25519); + nhlog::crypto()->info("curve25519: {}", olm::client()->identity_keys().curve25519); // Upload one time keys for the device. - log::crypto()->info("generating one time keys"); + nhlog::crypto()->info("generating one time keys"); olm::client()->generate_one_time_keys(MAX_ONETIME_KEYS); http::v2::client()->upload_keys( @@ -1029,9 +1030,9 @@ ChatPage::tryInitialSync() [this](const mtx::responses::UploadKeys &res, mtx::http::RequestErr err) { if (err) { const int status_code = static_cast(err->status_code); - log::crypto()->critical("failed to upload one time keys: {} {}", - err->matrix_error.error, - status_code); + nhlog::crypto()->critical("failed to upload one time keys: {} {}", + err->matrix_error.error, + status_code); // TODO We should have a timeout instead of keeping hammering the server. emit tryInitialSyncCb(); return; @@ -1039,10 +1040,10 @@ ChatPage::tryInitialSync() olm::client()->mark_keys_as_published(); for (const auto &entry : res.one_time_key_counts) - log::net()->info( + nhlog::net()->info( "uploaded {} {} one-time keys", entry.second, entry.first); - log::net()->info("trying initial sync"); + nhlog::net()->info("trying initial sync"); mtx::http::SyncOpts opts; opts.timeout = 0; @@ -1065,7 +1066,7 @@ ChatPage::trySync() try { opts.since = cache::client()->nextBatchToken(); } catch (const lmdb::error &e) { - log::db()->error("failed to retrieve next batch token: {}", e.what()); + nhlog::db()->error("failed to retrieve next batch token: {}", e.what()); return; } @@ -1077,7 +1078,7 @@ ChatPage::trySync() const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); const int status_code = static_cast(err->status_code); - log::net()->error("sync error: {} {}", status_code, err_code); + nhlog::net()->error("sync error: {} {}", status_code, err_code); if (status_code <= 0 || status_code >= 600) { if (!http::v2::is_logged_in()) @@ -1109,7 +1110,7 @@ ChatPage::trySync() } } - log::net()->debug("sync completed: {}", res.next_batch); + nhlog::net()->debug("sync completed: {}", res.next_batch); // Ensure that we have enough one-time keys available. ensureOneTimeKeyCount(res.device_one_time_keys_count); @@ -1126,7 +1127,7 @@ ChatPage::trySync() emit syncTopBar(updates); emit syncRoomlist(updates); } catch (const lmdb::error &e) { - log::db()->error("saving sync response: {}", e.what()); + nhlog::db()->error("saving sync response: {}", e.what()); } emit trySyncCb(); @@ -1201,8 +1202,8 @@ ChatPage::sendTypingNotifications() http::v2::client()->start_typing( current_room_.toStdString(), 10'000, [](mtx::http::RequestErr err) { if (err) { - log::net()->warn("failed to send typing notification: {}", - err->matrix_error.error); + nhlog::net()->warn("failed to send typing notification: {}", + err->matrix_error.error); } }); } @@ -1216,7 +1217,7 @@ ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::Request const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); const int status_code = static_cast(err->status_code); - log::net()->error("sync error: {} {}", status_code, err_code); + nhlog::net()->error("sync error: {} {}", status_code, err_code); switch (status_code) { case 502: @@ -1232,7 +1233,7 @@ ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::Request } } - log::net()->info("initial sync completed"); + nhlog::net()->info("initial sync completed"); try { cache::client()->saveState(res); @@ -1242,7 +1243,7 @@ ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::Request emit initializeViews(std::move(res.rooms)); emit initializeRoomList(cache::client()->roomInfo()); } catch (const lmdb::error &e) { - log::db()->error("{}", e.what()); + nhlog::db()->error("{}", e.what()); emit tryInitialSyncCb(); return; } @@ -1258,14 +1259,14 @@ ChatPage::ensureOneTimeKeyCount(const std::map &counts) if (entry.second < MAX_ONETIME_KEYS) { const int nkeys = MAX_ONETIME_KEYS - entry.second; - log::crypto()->info("uploading {} {} keys", nkeys, entry.first); + nhlog::crypto()->info("uploading {} {} keys", nkeys, entry.first); olm::client()->generate_one_time_keys(nkeys); http::v2::client()->upload_keys( olm::client()->create_upload_keys_request(), [](const mtx::responses::UploadKeys &, mtx::http::RequestErr err) { if (err) { - log::crypto()->warn( + nhlog::crypto()->warn( "failed to update one-time keys: {} {}", err->matrix_error.error, static_cast(err->status_code)); @@ -1287,7 +1288,7 @@ ChatPage::getProfileInfo() http::v2::client()->get_profile( userid, [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) { if (err) { - log::net()->warn("failed to retrieve own profile info"); + nhlog::net()->warn("failed to retrieve own profile info"); return; } @@ -1311,7 +1312,7 @@ ChatPage::getProfileInfo() const std::string &, mtx::http::RequestErr err) { if (err) { - log::net()->warn( + nhlog::net()->warn( "failed to download user avatar: {} - {}", mtx::errors::to_string(err->matrix_error.errcode), err->matrix_error.error); diff --git a/src/CommunitiesList.cc b/src/CommunitiesList.cc index 49affcb7..df4d6361 100644 --- a/src/CommunitiesList.cc +++ b/src/CommunitiesList.cc @@ -136,7 +136,7 @@ CommunitiesList::fetchCommunityAvatar(const QString &id, const QString &avatarUr http::v2::client()->get_thumbnail( opts, [this, opts, id](const std::string &res, mtx::http::RequestErr err) { if (err) { - log::net()->warn("failed to download avatar: {} - ({} {})", + nhlog::net()->warn("failed to download avatar: {} - ({} {})", opts.mxc_url, mtx::errors::to_string(err->matrix_error.errcode), err->matrix_error.error); diff --git a/src/Logging.cpp b/src/Logging.cpp index 77e61e09..bccbe389 100644 --- a/src/Logging.cpp +++ b/src/Logging.cpp @@ -7,13 +7,13 @@ namespace { std::shared_ptr db_logger = nullptr; std::shared_ptr net_logger = nullptr; std::shared_ptr crypto_logger = nullptr; -std::shared_ptr main_logger = nullptr; +std::shared_ptr ui_logger = nullptr; constexpr auto MAX_FILE_SIZE = 1024 * 1024 * 6; constexpr auto MAX_LOG_FILES = 3; } -namespace log { +namespace nhlog { void init(const std::string &file_path) { @@ -26,17 +26,17 @@ init(const std::string &file_path) sinks.push_back(file_sink); sinks.push_back(console_sink); - net_logger = std::make_shared("net", std::begin(sinks), std::end(sinks)); - main_logger = std::make_shared("main", std::begin(sinks), std::end(sinks)); - db_logger = std::make_shared("db", std::begin(sinks), std::end(sinks)); + net_logger = std::make_shared("net", std::begin(sinks), std::end(sinks)); + ui_logger = std::make_shared("ui", std::begin(sinks), std::end(sinks)); + db_logger = std::make_shared("db", std::begin(sinks), std::end(sinks)); crypto_logger = std::make_shared("crypto", std::begin(sinks), std::end(sinks)); } std::shared_ptr -main() +ui() { - return main_logger; + return ui_logger; } std::shared_ptr diff --git a/src/MainWindow.cc b/src/MainWindow.cc index cca51f03..088bb5c0 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -155,7 +155,7 @@ MainWindow::MainWindow(QWidget *parent) using namespace mtx::identifiers; http::v2::client()->set_user(parse(user_id.toStdString())); } catch (const std::invalid_argument &e) { - log::main()->critical("bootstrapped with invalid user_id: {}", + nhlog::ui()->critical("bootstrapped with invalid user_id: {}", user_id.toStdString()); } diff --git a/src/Olm.cpp b/src/Olm.cpp index 6e130277..f39554f0 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -23,17 +23,17 @@ handle_to_device_messages(const std::vector &msgs) if (msgs.empty()) return; - log::crypto()->info("received {} to_device messages", msgs.size()); + nhlog::crypto()->info("received {} to_device messages", msgs.size()); for (const auto &msg : msgs) { try { OlmMessage olm_msg = msg; handle_olm_message(std::move(olm_msg)); } catch (const nlohmann::json::exception &e) { - log::crypto()->warn( + nhlog::crypto()->warn( "parsing error for olm message: {} {}", e.what(), msg.dump(2)); } catch (const std::invalid_argument &e) { - log::crypto()->warn( + nhlog::crypto()->warn( "validation error for olm message: {} {}", e.what(), msg.dump(2)); } } @@ -42,8 +42,8 @@ handle_to_device_messages(const std::vector &msgs) void handle_olm_message(const OlmMessage &msg) { - log::crypto()->info("sender : {}", msg.sender); - log::crypto()->info("sender_key: {}", msg.sender_key); + nhlog::crypto()->info("sender : {}", msg.sender); + nhlog::crypto()->info("sender_key: {}", msg.sender_key); const auto my_key = olm::client()->identity_keys().curve25519; @@ -53,7 +53,7 @@ handle_olm_message(const OlmMessage &msg) continue; const auto type = cipher.second.type; - log::crypto()->info("type: {}", type == 0 ? "OLM_PRE_KEY" : "OLM_MESSAGE"); + nhlog::crypto()->info("type: {}", type == 0 ? "OLM_PRE_KEY" : "OLM_MESSAGE"); if (type == OLM_MESSAGE_TYPE_PRE_KEY) handle_pre_key_olm_message(msg.sender, msg.sender_key, cipher.second); @@ -67,19 +67,20 @@ handle_pre_key_olm_message(const std::string &sender, const std::string &sender_key, const OlmCipherContent &content) { - log::crypto()->info("opening olm session with {}", sender); + nhlog::crypto()->info("opening olm session with {}", sender); OlmSessionPtr inbound_session = nullptr; try { inbound_session = olm::client()->create_inbound_session(content.body); } catch (const olm_exception &e) { - log::crypto()->critical( + nhlog::crypto()->critical( "failed to create inbound session with {}: {}", sender, e.what()); return; } if (!matches_inbound_session_from(inbound_session.get(), sender_key, content.body)) { - log::crypto()->warn("inbound olm session doesn't match sender's key ({})", sender); + nhlog::crypto()->warn("inbound olm session doesn't match sender's key ({})", + sender); return; } @@ -88,13 +89,13 @@ handle_pre_key_olm_message(const std::string &sender, output = olm::client()->decrypt_message( inbound_session.get(), OLM_MESSAGE_TYPE_PRE_KEY, content.body); } catch (const olm_exception &e) { - log::crypto()->critical( + nhlog::crypto()->critical( "failed to decrypt olm message {}: {}", content.body, e.what()); return; } auto plaintext = json::parse(std::string((char *)output.data(), output.size())); - log::crypto()->info("decrypted message: \n {}", plaintext.dump(2)); + nhlog::crypto()->info("decrypted message: \n {}", plaintext.dump(2)); std::string room_id, session_id, session_key; try { @@ -102,7 +103,7 @@ handle_pre_key_olm_message(const std::string &sender, session_id = plaintext.at("content").at("session_id"); session_key = plaintext.at("content").at("session_key"); } catch (const nlohmann::json::exception &e) { - log::crypto()->critical( + nhlog::crypto()->critical( "failed to parse plaintext olm message: {} {}", e.what(), plaintext.dump(2)); return; } @@ -118,14 +119,15 @@ handle_pre_key_olm_message(const std::string &sender, try { cache::client()->saveInboundMegolmSession(index, std::move(megolm_session)); } catch (const lmdb::error &e) { - log::crypto()->critical("failed to save inbound megolm session: {}", - e.what()); + nhlog::crypto()->critical("failed to save inbound megolm session: {}", + e.what()); return; } - log::crypto()->info("established inbound megolm session ({}, {})", room_id, sender); + nhlog::crypto()->info( + "established inbound megolm session ({}, {})", room_id, sender); } else { - log::crypto()->warn( + nhlog::crypto()->warn( "inbound megolm session already exists ({}, {})", room_id, sender); } } @@ -133,7 +135,7 @@ handle_pre_key_olm_message(const std::string &sender, void handle_olm_normal_message(const std::string &, const std::string &, const OlmCipherContent &) { - log::crypto()->warn("olm(1) not implemeted yet"); + nhlog::crypto()->warn("olm(1) not implemeted yet"); } mtx::events::msg::Encrypted @@ -155,7 +157,7 @@ encrypt_group_message(const std::string &room_id, data.device_id = device_id; auto message_index = olm_outbound_group_session_message_index(res.session); - log::crypto()->info("next message_index {}", message_index); + nhlog::crypto()->info("next message_index {}", message_index); // We need to re-pickle the session after we send a message to save the new message_index. cache::client()->updateOutboundMegolmSession(room_id, message_index); diff --git a/src/RegisterPage.cc b/src/RegisterPage.cc index a5960b83..f56cd663 100644 --- a/src/RegisterPage.cc +++ b/src/RegisterPage.cc @@ -153,7 +153,7 @@ RegisterPage::RegisterPage(QWidget *parent) [this](const mtx::responses::Register &res, mtx::http::RequestErr err) { if (err) { - log::net()->warn( + nhlog::net()->warn( "failed to retrieve registration flows: {}", err->matrix_error.error); emit errorOccurred(); @@ -231,7 +231,7 @@ RegisterPage::onRegisterButtonClicked() const mtx::responses::RegistrationFlows &res, mtx::http::RequestErr err) { if (res.session.empty() && err) { - log::net()->warn( + nhlog::net()->warn( "failed to retrieve registration flows: ({}) " "{}", static_cast(err->status_code), @@ -247,7 +247,7 @@ RegisterPage::onRegisterButtonClicked() return; } - log::net()->warn("failed to register: status_code ({})", + nhlog::net()->warn("failed to register: status_code ({})", static_cast(err->status_code)); emit registerErrorCb(QString::fromStdString(err->matrix_error.error)); diff --git a/src/RoomList.cc b/src/RoomList.cc index 4891f746..b5bcdad6 100644 --- a/src/RoomList.cc +++ b/src/RoomList.cc @@ -95,7 +95,7 @@ RoomList::updateAvatar(const QString &room_id, const QString &url) http::v2::client()->get_thumbnail( opts, [room_id, opts, this](const std::string &res, mtx::http::RequestErr err) { if (err) { - log::net()->warn( + nhlog::net()->warn( "failed to download room avatar: {} {} {}", opts.mxc_url, mtx::errors::to_string(err->matrix_error.errcode), @@ -141,7 +141,7 @@ void RoomList::updateUnreadMessageCount(const QString &roomid, int count) { if (!roomExists(roomid)) { - log::main()->warn("updateUnreadMessageCount: unknown room_id {}", + nhlog::ui()->warn("updateUnreadMessageCount: unknown room_id {}", roomid.toStdString()); return; } @@ -167,7 +167,7 @@ RoomList::calculateUnreadMessageCount() void RoomList::initialize(const QMap &info) { - log::main()->info("initialize room list"); + nhlog::ui()->info("initialize room list"); rooms_.clear(); @@ -220,7 +220,7 @@ RoomList::highlightSelectedRoom(const QString &room_id) emit roomChanged(room_id); if (!roomExists(room_id)) { - log::main()->warn("roomlist: clicked unknown room_id"); + nhlog::ui()->warn("roomlist: clicked unknown room_id"); return; } @@ -243,7 +243,7 @@ void RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img) { if (!roomExists(roomid)) { - log::main()->warn("avatar update on non-existent room_id: {}", + nhlog::ui()->warn("avatar update on non-existent room_id: {}", roomid.toStdString()); return; } @@ -258,7 +258,7 @@ void RoomList::updateRoomDescription(const QString &roomid, const DescInfo &info) { if (!roomExists(roomid)) { - log::main()->warn("description update on non-existent room_id: {}, {}", + nhlog::ui()->warn("description update on non-existent room_id: {}, {}", roomid.toStdString(), info.body.toStdString()); return; diff --git a/src/dialogs/PreviewUploadOverlay.cc b/src/dialogs/PreviewUploadOverlay.cc index 3c44e911..db1e31f2 100644 --- a/src/dialogs/PreviewUploadOverlay.cc +++ b/src/dialogs/PreviewUploadOverlay.cc @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -25,6 +24,7 @@ #include #include "Config.h" +#include "Logging.hpp" #include "Utils.h" #include "dialogs/PreviewUploadOverlay.h" @@ -142,8 +142,9 @@ PreviewUploadOverlay::setPreview(const QString &path) QFile file{path}; if (!file.open(QIODevice::ReadOnly)) { - qWarning() << "Failed to open file from:" << path; - qWarning() << "Reason:" << file.errorString(); + nhlog::ui()->warn("Failed to open file ({}): {}", + path.toStdString(), + file.errorString().toStdString()); close(); return; } @@ -152,7 +153,7 @@ PreviewUploadOverlay::setPreview(const QString &path) auto mime = db.mimeTypeForFileNameAndData(path, &file); if ((data_ = file.readAll()).isEmpty()) { - qWarning() << "Failed to read media:" << file.errorString(); + nhlog::ui()->warn("Failed to read media: {}", file.errorString().toStdString()); close(); return; } diff --git a/src/dialogs/RoomSettings.cpp b/src/dialogs/RoomSettings.cpp index a091c8bc..74d08478 100644 --- a/src/dialogs/RoomSettings.cpp +++ b/src/dialogs/RoomSettings.cpp @@ -338,7 +338,7 @@ RoomSettings::setupEditButton() hasEditRights_ = cache::client()->hasEnoughPowerLevel( {EventType::RoomName, EventType::RoomTopic}, room_id_.toStdString(), userId); } catch (const lmdb::error &e) { - qWarning() << "lmdb error" << e.what(); + nhlog::db()->warn("lmdb error: {}", e.what()); } constexpr int buttonSize = 36; @@ -379,7 +379,8 @@ RoomSettings::retrieveRoomInfo() info_ = cache::client()->singleRoomInfo(room_id_.toStdString()); setAvatar(QImage::fromData(cache::client()->image(info_.avatar_url))); } catch (const lmdb::error &e) { - qWarning() << "failed to retrieve room info from cache" << room_id_; + nhlog::db()->warn("failed to retrieve room info from cache: {}", + room_id_.toStdString()); } } @@ -415,17 +416,17 @@ RoomSettings::enableEncryption() room_id, [room_id, this](const mtx::responses::EventId &, mtx::http::RequestErr err) { if (err) { int status_code = static_cast(err->status_code); - log::net()->warn("failed to enable encryption in room ({}): {} {}", - room_id, - err->matrix_error.error, - status_code); + nhlog::net()->warn("failed to enable encryption in room ({}): {} {}", + room_id, + err->matrix_error.error, + status_code); emit enableEncryptionError( tr("Failed to enable encryption: %1") .arg(QString::fromStdString(err->matrix_error.error))); return; } - log::net()->info("enabled encryption on room ({})", room_id); + nhlog::net()->info("enabled encryption on room ({})", room_id); }); } diff --git a/src/main.cc b/src/main.cc index 0a127962..24524c2c 100644 --- a/src/main.cc +++ b/src/main.cc @@ -127,9 +127,9 @@ main(int argc, char *argv[]) createCacheDirectory(); try { - log::init(QString("%1/nheko.log") - .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) - .toStdString()); + nhlog::init(QString("%1/nheko.log") + .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) + .toStdString()); } catch (const spdlog::spdlog_ex &ex) { std::cout << "Log initialization failed: " << ex.what() << std::endl; std::exit(1); @@ -171,7 +171,7 @@ main(int argc, char *argv[]) } }); - log::main()->info("starting nheko {}", nheko::version); + nhlog::ui()->info("starting nheko {}", nheko::version); return app.exec(); } diff --git a/src/timeline/TimelineItem.cc b/src/timeline/TimelineItem.cc index 83a0aaed..3505d347 100644 --- a/src/timeline/TimelineItem.cc +++ b/src/timeline/TimelineItem.cc @@ -23,6 +23,7 @@ #include "Avatar.h" #include "ChatPage.h" #include "Config.h" +#include "Logging.hpp" #include "timeline/TimelineItem.h" #include "timeline/widgets/AudioItem.h" @@ -653,3 +654,19 @@ TimelineItem::addAvatar() AvatarProvider::resolve( room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); }); } + +void +TimelineItem::sendReadReceipt() const +{ + if (!event_id_.isEmpty()) + http::v2::client()->read_event(room_id_.toStdString(), + event_id_.toStdString(), + [this](mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to read_event ({}, {})", + room_id_.toStdString(), + event_id_.toStdString()); + } + }); +} diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc index 9276a7bc..8f3ad1a7 100644 --- a/src/timeline/TimelineView.cc +++ b/src/timeline/TimelineView.cc @@ -301,8 +301,8 @@ TimelineView::parseMessageEvent(const mtx::events::collections::TimelineEvents & try { cache::client()->setEncryptedRoom(room_id_.toStdString()); } catch (const lmdb::error &e) { - log::db()->critical("failed to save room {} as encrypted", - room_id_.toStdString()); + nhlog::db()->critical("failed to save room {} as encrypted", + room_id_.toStdString()); } } @@ -324,10 +324,10 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEventinboundMegolmSessionExists(index)) { - log::crypto()->info("Could not find inbound megolm session ({}, {}, {})", - index.room_id, - index.session_id, - e.sender); + nhlog::crypto()->info("Could not find inbound megolm session ({}, {}, {})", + index.room_id, + index.session_id, + e.sender); // TODO: request megolm session_id & session_key from the sender. return dummy; } @@ -344,7 +344,7 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEventinfo("decrypted data: \n {}", body.dump(2)); + nhlog::crypto()->info("decrypted data: \n {}", body.dump(2)); json event_array = json::array(); event_array.push_back(body); @@ -523,10 +523,10 @@ TimelineView::getMessages() http::v2::client()->messages( opts, [this, opts](const mtx::responses::Messages &res, mtx::http::RequestErr err) { if (err) { - log::net()->error("failed to call /messages ({}): {} - {}", - opts.room_id, - mtx::errors::to_string(err->matrix_error.errcode), - err->matrix_error.error); + nhlog::net()->error("failed to call /messages ({}): {} - {}", + opts.room_id, + mtx::errors::to_string(err->matrix_error.errcode), + err->matrix_error.error); return; } @@ -607,7 +607,7 @@ TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction) void TimelineView::updatePendingMessage(const std::string &txn_id, const QString &event_id) { - log::main()->info("[{}] message was received by the server", txn_id); + nhlog::ui()->info("[{}] message was received by the server", txn_id); if (!pending_msgs_.isEmpty() && pending_msgs_.head().txn_id == txn_id) { // We haven't received it yet auto msg = pending_msgs_.dequeue(); @@ -642,7 +642,7 @@ TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body) try { message.is_encrypted = cache::client()->isRoomEncrypted(room_id_.toStdString()); } catch (const lmdb::error &e) { - log::db()->critical("failed to check encryption status of room {}", e.what()); + nhlog::db()->critical("failed to check encryption status of room {}", e.what()); view_item->deleteLater(); // TODO: Send a notification to the user. @@ -676,11 +676,11 @@ TimelineView::sendNextPendingMessage() PendingMessage &m = pending_msgs_.head(); - log::main()->info("[{}] sending next queued message", m.txn_id); + nhlog::ui()->info("[{}] sending next queued message", m.txn_id); if (m.is_encrypted) { prepareEncryptedMessage(std::move(m)); - log::main()->info("[{}] sending encrypted event", m.txn_id); + nhlog::ui()->info("[{}] sending encrypted event", m.txn_id); return; } @@ -763,7 +763,7 @@ TimelineView::sendNextPendingMessage() break; } default: - log::main()->warn("cannot send unknown message type: {}", m.body.toStdString()); + nhlog::ui()->warn("cannot send unknown message type: {}", m.body.toStdString()); break; } } @@ -777,7 +777,7 @@ TimelineView::notifyForLastEvent() if (lastTimelineItem) emit updateLastTimelineMessage(room_id_, lastTimelineItem->descriptionMessage()); else - log::main()->warn("cast to TimelineView failed: {}", room_id_.toStdString()); + nhlog::ui()->warn("cast to TimelineView failed: {}", room_id_.toStdString()); } void @@ -817,7 +817,7 @@ TimelineView::removePendingMessage(const std::string &txn_id) if (pending_sent_msgs_.isEmpty()) sendNextPendingMessage(); - log::main()->info("[{}] removed message with sync", txn_id); + nhlog::ui()->info("[{}] removed message with sync", txn_id); } } for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); ++it) { @@ -825,7 +825,7 @@ TimelineView::removePendingMessage(const std::string &txn_id) int index = std::distance(pending_msgs_.begin(), it); pending_msgs_.removeAt(index); - log::main()->info("[{}] removed message before sync", txn_id); + nhlog::ui()->info("[{}] removed message before sync", txn_id); return; } } @@ -861,7 +861,7 @@ TimelineView::readLastEvent() const eventId.toStdString(), [this, eventId](mtx::http::RequestErr err) { if (err) { - log::net()->warn( + nhlog::net()->warn( "failed to read event ({}, {})", room_id_.toStdString(), eventId.toStdString()); @@ -936,7 +936,7 @@ void TimelineView::removeEvent(const QString &event_id) { if (!eventIds_.contains(event_id)) { - log::main()->warn("cannot remove widget with unknown event_id: {}", + nhlog::ui()->warn("cannot remove widget with unknown event_id: {}", event_id.toStdString()); return; } @@ -1062,10 +1062,10 @@ TimelineView::sendRoomMessageHandler(const std::string &txn_id, { if (err) { const int status_code = static_cast(err->status_code); - log::net()->warn("[{}] failed to send message: {} {}", - txn_id, - err->matrix_error.error, - status_code); + nhlog::net()->warn("[{}] failed to send message: {} {}", + txn_id, + err->matrix_error.error, + status_code); emit messageFailed(txn_id); return; } @@ -1200,7 +1200,7 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) return; } - log::main()->info("creating new outbound megolm session"); + nhlog::ui()->info("creating new outbound megolm session"); // Create a new outbound megolm session. auto outbound_session = olm::client()->init_outbound_group_session(); @@ -1223,7 +1223,7 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) room_id, session_data, std::move(outbound_session)); const auto members = cache::client()->roomMembers(room_id); - log::main()->info("retrieved {} members for {}", members.size(), room_id); + nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id); auto keeper = std::make_shared( [megolm_payload, room_id, doc, txn_id = msg.txn_id, this]() { @@ -1243,8 +1243,8 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) std::placeholders::_2)); } catch (const lmdb::error &e) { - log::db()->critical("failed to save megolm outbound session: {}", - e.what()); + nhlog::db()->critical( + "failed to save megolm outbound session: {}", e.what()); } }); @@ -1257,16 +1257,16 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) [keeper = std::move(keeper), megolm_payload](const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { if (err) { - log::net()->warn("failed to query device keys: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); + nhlog::net()->warn("failed to query device keys: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); // TODO: Mark the event as failed. Communicate with the UI. return; } for (const auto &entry : res.device_keys) { for (const auto &dev : entry.second) { - log::net()->info("received device {}", dev.first); + nhlog::net()->info("received device {}", dev.first); const auto device_keys = dev.second.keys; const auto curveKey = "curve25519:" + dev.first; @@ -1274,7 +1274,7 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) if ((device_keys.find(curveKey) == device_keys.end()) || (device_keys.find(edKey) == device_keys.end())) { - log::net()->info( + nhlog::net()->info( "ignoring malformed keys for device {}", dev.first); continue; @@ -1286,7 +1286,7 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) // Validate signatures for (const auto &algo : dev.second.keys) { - log::net()->info( + nhlog::net()->info( "dev keys {} {}", algo.first, algo.second); } @@ -1308,22 +1308,22 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) const mtx::responses::ClaimKeys &res, mtx::http::RequestErr err) { if (err) { - log::net()->warn( + nhlog::net()->warn( "claim keys error: {}", err->matrix_error.error); return; } - log::net()->info("claimed keys for {} - {}", - user_id, - device_id); + nhlog::net()->info("claimed keys for {} - {}", + user_id, + device_id); auto retrieved_devices = res.one_time_keys.at(user_id); for (const auto &rd : retrieved_devices) { - log::net()->info("{} : \n {}", - rd.first, - rd.second.dump(2)); + nhlog::net()->info("{} : \n {}", + rd.first, + rd.second.dump(2)); // TODO: Verify signatures auto otk = rd.second.begin()->at("key"); @@ -1351,7 +1351,7 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) body, [keeper](mtx::http::RequestErr err) { if (err) { - log::net()->warn( + nhlog::net()->warn( "failed to send " "send_to_device " "message: {}", @@ -1366,7 +1366,7 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) }); } catch (const lmdb::error &e) { - log::db()->critical( + nhlog::db()->critical( "failed to open outbound megolm session ({}): {}", room_id, e.what()); return; } diff --git a/src/timeline/TimelineViewManager.cc b/src/timeline/TimelineViewManager.cc index b6e7d50d..7ea1ee4a 100644 --- a/src/timeline/TimelineViewManager.cc +++ b/src/timeline/TimelineViewManager.cc @@ -18,10 +18,10 @@ #include #include -#include #include #include +#include "Logging.hpp" #include "timeline/TimelineView.h" #include "timeline/TimelineViewManager.h" #include "timeline/widgets/AudioItem.h" @@ -76,7 +76,7 @@ TimelineViewManager::queueImageMessage(const QString &roomid, uint64_t size) { if (!timelineViewExists(roomid)) { - qDebug() << "Cannot send m.image message to a non-managed view"; + nhlog::ui()->warn("Cannot send m.image message to a non-managed view"); return; } @@ -93,7 +93,7 @@ TimelineViewManager::queueFileMessage(const QString &roomid, uint64_t size) { if (!timelineViewExists(roomid)) { - qDebug() << "Cannot send m.file message to a non-managed view"; + nhlog::ui()->warn("cannot send m.file message to a non-managed view"); return; } @@ -110,7 +110,7 @@ TimelineViewManager::queueAudioMessage(const QString &roomid, uint64_t size) { if (!timelineViewExists(roomid)) { - qDebug() << "Cannot send m.audio message to a non-managed view"; + nhlog::ui()->warn("cannot send m.audio message to a non-managed view"); return; } @@ -127,7 +127,7 @@ TimelineViewManager::queueVideoMessage(const QString &roomid, uint64_t size) { if (!timelineViewExists(roomid)) { - qDebug() << "Cannot send m.video message to a non-managed view"; + nhlog::ui()->warn("cannot send m.video message to a non-managed view"); return; } @@ -198,7 +198,8 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms) auto roomid = QString::fromStdString(room.first); if (!timelineViewExists(roomid)) { - qDebug() << "Ignoring event from unknown room" << roomid; + nhlog::ui()->warn("ignoring event from unknown room: {}", + roomid.toStdString()); continue; } @@ -212,7 +213,8 @@ void TimelineViewManager::setHistoryView(const QString &room_id) { if (!timelineViewExists(room_id)) { - qDebug() << "Room ID from RoomList is not present in ViewManager" << room_id; + nhlog::ui()->warn("room from RoomList is not present in ViewManager: {}", + room_id.toStdString()); return; } diff --git a/src/timeline/widgets/AudioItem.cc b/src/timeline/widgets/AudioItem.cc index 1ad47747..7cbbed28 100644 --- a/src/timeline/widgets/AudioItem.cc +++ b/src/timeline/widgets/AudioItem.cc @@ -16,13 +16,13 @@ */ #include -#include #include #include #include #include #include +#include "Logging.hpp" #include "MatrixClient.h" #include "Utils.h" @@ -127,7 +127,8 @@ AudioItem::mousePressEvent(QMouseEvent *event) const std::string &, mtx::http::RequestErr err) { if (err) { - qWarning() << "failed to retrieve m.audio content:" << url_; + nhlog::net()->info("failed to retrieve m.audio content: {}", + url_.toString().toStdString()); return; } @@ -147,8 +148,8 @@ AudioItem::fileDownloaded(const QByteArray &data) file.write(data); file.close(); - } catch (const std::exception &ex) { - qDebug() << "Error while saving file to:" << ex.what(); + } catch (const std::exception &e) { + nhlog::ui()->warn("error while saving file: {}", e.what()); } } diff --git a/src/timeline/widgets/FileItem.cc b/src/timeline/widgets/FileItem.cc index 43689243..4ce4d256 100644 --- a/src/timeline/widgets/FileItem.cc +++ b/src/timeline/widgets/FileItem.cc @@ -16,13 +16,13 @@ */ #include -#include #include #include #include #include #include +#include "Logging.hpp" #include "MatrixClient.h" #include "Utils.h" @@ -89,7 +89,7 @@ FileItem::openUrl() .arg(QString::fromStdString(mxc_parts.media_id)); if (!QDesktopServices::openUrl(urlToOpen)) - qWarning() << "Could not open url" << urlToOpen; + nhlog::ui()->warn("Could not open url: {}", urlToOpen.toStdString()); } QSize @@ -121,7 +121,8 @@ FileItem::mousePressEvent(QMouseEvent *event) const std::string &, mtx::http::RequestErr err) { if (err) { - qWarning() << "failed to retrieve m.file content:" << url_; + nhlog::ui()->warn("failed to retrieve m.file content: {}", + url_.toString().toStdString()); return; } @@ -143,8 +144,8 @@ FileItem::fileDownloaded(const QByteArray &data) file.write(data); file.close(); - } catch (const std::exception &ex) { - qDebug() << "Error while saving file to:" << ex.what(); + } catch (const std::exception &e) { + nhlog::ui()->warn("Error while saving file to: {}", e.what()); } } diff --git a/src/timeline/widgets/ImageItem.cc b/src/timeline/widgets/ImageItem.cc index 6aa010a4..bf1c05d6 100644 --- a/src/timeline/widgets/ImageItem.cc +++ b/src/timeline/widgets/ImageItem.cc @@ -16,7 +16,6 @@ */ #include -#include #include #include #include @@ -25,6 +24,7 @@ #include #include "Config.h" +#include "Logging.hpp" #include "MatrixClient.h" #include "Utils.h" #include "dialogs/ImageOverlay.h" @@ -39,8 +39,11 @@ ImageItem::downloadMedia(const QUrl &url) const std::string &, mtx::http::RequestErr err) { if (err) { - qWarning() - << "failed to retrieve image:" << url; + nhlog::net()->warn( + "failed to retrieve image {}: {} {}", + url.toString().toStdString(), + err->matrix_error.error, + static_cast(err->status_code)); return; } @@ -61,8 +64,8 @@ ImageItem::saveImage(const QString &filename, const QByteArray &data) file.write(data); file.close(); - } catch (const std::exception &ex) { - qDebug() << "Error while saving file to:" << ex.what(); + } catch (const std::exception &e) { + nhlog::ui()->warn("Error while saving file to: {}", e.what()); } } @@ -111,7 +114,7 @@ ImageItem::openUrl() .arg(QString::fromStdString(mxc_parts.media_id)); if (!QDesktopServices::openUrl(urlToOpen)) - qWarning() << "Could not open url" << urlToOpen; + nhlog::ui()->warn("could not open url: {}", urlToOpen.toStdString()); } QSize @@ -239,14 +242,19 @@ ImageItem::saveAs() if (filename.isEmpty()) return; + const auto url = url_.toString().toStdString(); + http::v2::client()->download( - url_.toString().toStdString(), - [this, filename](const std::string &data, - const std::string &, - const std::string &, - mtx::http::RequestErr err) { + url, + [this, filename, url](const std::string &data, + const std::string &, + const std::string &, + mtx::http::RequestErr err) { if (err) { - qWarning() << "failed to retrieve image:" << url_; + nhlog::net()->warn("failed to retrieve image {}: {} {}", + url, + err->matrix_error.error, + static_cast(err->status_code)); return; } diff --git a/src/timeline/widgets/VideoItem.cc b/src/timeline/widgets/VideoItem.cc index c1f68847..34d963a9 100644 --- a/src/timeline/widgets/VideoItem.cc +++ b/src/timeline/widgets/VideoItem.cc @@ -15,7 +15,6 @@ * along with this program. If not, see . */ -#include #include #include -- cgit 1.5.1