summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AliasEditModel.cpp1
-rw-r--r--src/AliasEditModel.h2
-rw-r--r--src/AvatarProvider.cpp4
-rw-r--r--src/AvatarProvider.h2
-rw-r--r--src/BlurhashProvider.cpp2
-rw-r--r--src/Cache.cpp245
-rw-r--r--src/Cache_p.h221
-rw-r--r--src/ChatPage.cpp14
-rw-r--r--src/CompletionProxyModel.cpp1
-rw-r--r--src/EventAccessors.cpp1
-rw-r--r--src/InviteesModel.cpp1
-rw-r--r--src/JdenticonProvider.cpp3
-rw-r--r--src/JdenticonProvider.h2
-rw-r--r--src/Logging.cpp1
-rw-r--r--src/Logging.h1
-rw-r--r--src/LoginPage.cpp1
-rw-r--r--src/MainWindow.h2
-rw-r--r--src/MatrixClient.cpp2
-rw-r--r--src/MemberList.cpp2
-rw-r--r--src/PowerlevelsEditModels.h3
-rw-r--r--src/RegisterPage.cpp1
-rw-r--r--src/RoomsModel.cpp1
-rw-r--r--src/SingleImagePackModel.cpp2
-rw-r--r--src/UserDirectoryModel.cpp1
-rw-r--r--src/UsersModel.cpp1
-rw-r--r--src/Utils.cpp14
-rw-r--r--src/Utils.h2
-rw-r--r--src/dbus/NhekoDBusBackend.cpp1
-rw-r--r--src/encryption/DeviceVerificationFlow.cpp12
-rw-r--r--src/encryption/DeviceVerificationFlow.h1
-rw-r--r--src/encryption/Olm.cpp1
-rw-r--r--src/encryption/Olm.h1
-rw-r--r--src/encryption/SelfVerificationStatus.cpp6
-rw-r--r--src/notifications/Manager.cpp1
-rw-r--r--src/timeline/EventStore.cpp2
-rw-r--r--src/timeline/InputBar.cpp6
-rw-r--r--src/timeline/TimelineModel.cpp4
-rw-r--r--src/ui/MxcMediaProxy.h2
-rw-r--r--src/ui/NhekoDropArea.cpp2
-rw-r--r--src/ui/RoomSettings.cpp1
40 files changed, 318 insertions, 255 deletions
diff --git a/src/AliasEditModel.cpp b/src/AliasEditModel.cpp
index c6dc35c6..cc6ea137 100644
--- a/src/AliasEditModel.cpp
+++ b/src/AliasEditModel.cpp
@@ -10,7 +10,6 @@
 
 #include <mtx/responses/common.hpp>
 
-#include "Cache.h"
 #include "Cache_p.h"
 #include "ChatPage.h"
 #include "Logging.h"
diff --git a/src/AliasEditModel.h b/src/AliasEditModel.h
index 04de5016..62771c8e 100644
--- a/src/AliasEditModel.h
+++ b/src/AliasEditModel.h
@@ -10,8 +10,6 @@
 
 #include <mtx/events/canonical_alias.hpp>
 
-#include "CacheStructs.h"
-
 class FetchPublishedAliasesJob final : public QObject
 {
     Q_OBJECT
diff --git a/src/AvatarProvider.cpp b/src/AvatarProvider.cpp
index b7dc3306..0e4195db 100644
--- a/src/AvatarProvider.cpp
+++ b/src/AvatarProvider.cpp
@@ -6,14 +6,10 @@
 #include <QPixmapCache>
 #include <QPointer>
 #include <memory>
-#include <unordered_map>
 
 #include "AvatarProvider.h"
 #include "Cache.h"
-#include "Logging.h"
-#include "MatrixClient.h"
 #include "MxcImageProvider.h"
-#include "Utils.h"
 
 static QPixmapCache avatar_cache;
 
diff --git a/src/AvatarProvider.h b/src/AvatarProvider.h
index 454ce957..b73b1ad6 100644
--- a/src/AvatarProvider.h
+++ b/src/AvatarProvider.h
@@ -4,7 +4,9 @@
 
 #pragma once
 
+#include <QObject>
 #include <QPixmap>
+
 #include <functional>
 
 using AvatarCallback = std::function<void(QPixmap)>;
diff --git a/src/BlurhashProvider.cpp b/src/BlurhashProvider.cpp
index 7ca491de..d4482419 100644
--- a/src/BlurhashProvider.cpp
+++ b/src/BlurhashProvider.cpp
@@ -4,8 +4,6 @@
 
 #include "BlurhashProvider.h"
 
-#include <algorithm>
-
 #include <QUrl>
 
 #include "blurhash.hpp"
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 25904e53..d7cd113b 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -24,6 +24,8 @@
 #include <qt6keychain/keychain.h>
 #endif
 
+#include <nlohmann/json.hpp>
+
 #include <mtx/responses/common.hpp>
 #include <mtx/responses/messages.hpp>
 
@@ -532,8 +534,8 @@ Cache::loadSecretsFromStore(
              name,
              toLoad,
              job,
-             name_    = name_,
-             internal = internal,
+             name__    = name_,
+             internal_ = internal,
              callback,
              databaseReadyOnFinished](QKeychain::Job *) mutable {
                 nhlog::db()->debug("Finished reading '{}'", toLoad.begin()->first);
@@ -549,7 +551,7 @@ Cache::loadSecretsFromStore(
                 if (secret.isEmpty()) {
                     nhlog::db()->debug("Restored empty secret '{}'.", name.toStdString());
                 } else {
-                    callback(name_, internal, secret.toStdString());
+                    callback(name__, internal_, secret.toStdString());
                 }
 
                 // load next secret
@@ -2042,6 +2044,213 @@ isMessage(const mtx::events::RoomEvent<mtx::events::voip::CallHangUp> &)
 // }
 }
 
+template<typename T>
+std::optional<mtx::events::StateEvent<T>>
+Cache::getStateEvent(lmdb::txn &txn, const std::string &room_id, std::string_view state_key)
+{
+    try {
+        constexpr auto type = mtx::events::state_content_to_type<T>;
+        static_assert(type != mtx::events::EventType::Unsupported,
+                      "Not a supported type in state events.");
+
+        if (room_id.empty())
+            return std::nullopt;
+        const auto typeStr = to_string(type);
+
+        std::string_view value;
+        if (state_key.empty()) {
+            auto db = getStatesDb(txn, room_id);
+            if (!db.get(txn, typeStr, value)) {
+                return std::nullopt;
+            }
+        } else {
+            auto db = getStatesKeyDb(txn, room_id);
+            // we can search using state key, since the compare functions defaults to the whole
+            // string, when there is no nullbyte
+            std::string_view data     = state_key;
+            std::string_view typeStrV = typeStr;
+
+            auto cursor = lmdb::cursor::open(txn, db);
+            if (!cursor.get(typeStrV, data, MDB_GET_BOTH))
+                return std::nullopt;
+
+            try {
+                auto eventsDb = getEventsDb(txn, room_id);
+                auto eventid  = data;
+                if (auto sep = data.rfind('\0'); sep != std::string_view::npos) {
+                    if (!eventsDb.get(txn, eventid.substr(sep + 1), value))
+                        return std::nullopt;
+                } else {
+                    return std::nullopt;
+                }
+
+            } catch (std::exception &) {
+                return std::nullopt;
+            }
+        }
+
+        return nlohmann::json::parse(value).get<mtx::events::StateEvent<T>>();
+    } catch (std::exception &) {
+        return std::nullopt;
+    }
+}
+
+template<typename T>
+std::vector<mtx::events::StateEvent<T>>
+Cache::getStateEventsWithType(lmdb::txn &txn,
+                              const std::string &room_id,
+                              mtx::events::EventType type)
+
+{
+    if (room_id.empty())
+        return {};
+
+    std::vector<mtx::events::StateEvent<T>> events;
+
+    {
+        auto db                   = getStatesKeyDb(txn, room_id);
+        auto eventsDb             = getEventsDb(txn, room_id);
+        const auto typeStr        = to_string(type);
+        std::string_view typeStrV = typeStr;
+        std::string_view data;
+        std::string_view value;
+
+        auto cursor = lmdb::cursor::open(txn, db);
+        bool first  = true;
+        if (cursor.get(typeStrV, data, MDB_SET)) {
+            while (cursor.get(typeStrV, data, first ? MDB_FIRST_DUP : MDB_NEXT_DUP)) {
+                first = false;
+
+                try {
+                    auto eventid = data;
+                    if (auto sep = data.rfind('\0'); sep != std::string_view::npos) {
+                        if (eventsDb.get(txn, eventid.substr(sep + 1), value))
+                            events.push_back(
+                              nlohmann::json::parse(value).get<mtx::events::StateEvent<T>>());
+                    }
+                } catch (std::exception &e) {
+                    nhlog::db()->warn("Failed to parse state event: {}", e.what());
+                }
+            }
+        }
+    }
+
+    return events;
+}
+
+template<class T>
+void
+Cache::saveStateEvents(lmdb::txn &txn,
+                       lmdb::dbi &statesdb,
+                       lmdb::dbi &stateskeydb,
+                       lmdb::dbi &membersdb,
+                       lmdb::dbi &eventsDb,
+                       const std::string &room_id,
+                       const std::vector<T> &events)
+{
+    for (const auto &e : events)
+        saveStateEvent(txn, statesdb, stateskeydb, membersdb, eventsDb, room_id, e);
+}
+
+template<class T>
+void
+Cache::saveStateEvent(lmdb::txn &txn,
+                      lmdb::dbi &statesdb,
+                      lmdb::dbi &stateskeydb,
+                      lmdb::dbi &membersdb,
+                      lmdb::dbi &eventsDb,
+                      const std::string &room_id,
+                      const T &event)
+{
+    using namespace mtx::events;
+    using namespace mtx::events::state;
+
+    if (auto e = std::get_if<StateEvent<Member>>(&event); e != nullptr) {
+        switch (e->content.membership) {
+        //
+        // We only keep users with invite or join membership.
+        //
+        case Membership::Invite:
+        case Membership::Join: {
+            auto display_name =
+              e->content.display_name.empty() ? e->state_key : e->content.display_name;
+
+            std::string inviter = "";
+            if (e->content.membership == mtx::events::state::Membership::Invite) {
+                inviter = e->sender;
+            }
+
+            // Lightweight representation of a member.
+            MemberInfo tmp{
+              display_name,
+              e->content.avatar_url,
+              inviter,
+              e->content.reason,
+              e->content.is_direct,
+            };
+
+            membersdb.put(txn, e->state_key, nlohmann::json(tmp).dump());
+            break;
+        }
+        default: {
+            membersdb.del(txn, e->state_key, "");
+            break;
+        }
+        }
+    } else if (auto encr = std::get_if<StateEvent<Encryption>>(&event)) {
+        if (!encr->state_key.empty())
+            return;
+
+        setEncryptedRoom(txn, room_id);
+
+        std::string_view temp;
+        // ensure we don't replace the event in the db
+        if (statesdb.get(txn, to_string(encr->type), temp)) {
+            return;
+        }
+    }
+
+    std::visit(
+      [&txn, &statesdb, &stateskeydb, &eventsDb, &membersdb](const auto &e) {
+          if constexpr (isStateEvent_<decltype(e)>) {
+              eventsDb.put(txn, e.event_id, nlohmann::json(e).dump());
+
+              if (e.type != EventType::Unsupported) {
+                  if (std::is_same_v<std::remove_cv_t<std::remove_reference_t<decltype(e)>>,
+                                     StateEvent<mtx::events::msg::Redacted>>) {
+                      // apply the redaction event
+                      if (e.type == EventType::RoomMember) {
+                          // membership is not revoked, but names are yeeted (so we set the name
+                          // to the mxid)
+                          MemberInfo tmp{e.state_key, ""};
+                          membersdb.put(txn, e.state_key, nlohmann::json(tmp).dump());
+                      } else if (e.state_key.empty()) {
+                          // strictly speaking some stuff in those events can be redacted, but
+                          // this is close enough. Ref:
+                          // https://spec.matrix.org/v1.6/rooms/v10/#redactions
+                          if (e.type != EventType::RoomCreate &&
+                              e.type != EventType::RoomJoinRules &&
+                              e.type != EventType::RoomPowerLevels &&
+                              e.type != EventType::RoomHistoryVisibility)
+                              statesdb.del(txn, to_string(e.type));
+                      } else
+                          stateskeydb.del(txn, to_string(e.type), e.state_key + '\0' + e.event_id);
+                  } else if (e.state_key.empty()) {
+                      statesdb.put(txn, to_string(e.type), nlohmann::json(e).dump());
+                  } else {
+                      auto data = e.state_key + '\0' + e.event_id;
+                      auto key  = to_string(e.type);
+
+                      // Work around https://bugs.openldap.org/show_bug.cgi?id=8447
+                      stateskeydb.del(txn, key, data);
+                      stateskeydb.put(txn, key, data);
+                  }
+              }
+          }
+      },
+      event);
+}
+
 void
 Cache::saveState(const mtx::responses::Sync &res)
 {
@@ -6004,3 +6213,33 @@ secret(const std::string &name)
     return instance_->secret(name);
 }
 } // namespace cache
+
+#define NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(Content)                                            \
+    template std::optional<mtx::events::StateEvent<Content>> Cache::getStateEvent(                 \
+      lmdb::txn &txn, const std::string &room_id, std::string_view state_key);                     \
+                                                                                                   \
+    template std::vector<mtx::events::StateEvent<Content>> Cache::getStateEventsWithType(          \
+      lmdb::txn &txn, const std::string &room_id, mtx::events::EventType type);
+
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Aliases)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Avatar)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::CanonicalAlias)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Create)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Encryption)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::GuestAccess)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::HistoryVisibility)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::JoinRules)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Member)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Name)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::PinnedEvents)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::PowerLevels)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Tombstone)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::ServerAcl)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Topic)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Widget)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::policy_rule::UserRule)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::policy_rule::RoomRule)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::policy_rule::ServerRule)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::space::Child)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::space::Parent)
+NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::msc2545::ImagePack)
diff --git a/src/Cache_p.h b/src/Cache_p.h
index e59796ed..d1316ae5 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -4,7 +4,6 @@
 
 #pragma once
 
-#include <limits>
 #include <optional>
 
 #include <QDateTime>
@@ -15,8 +14,8 @@
 #else
 #include <lmdb++.h>
 #endif
-#include <nlohmann/json.hpp>
 
+#include <mtx/events/collections.hpp>
 #include <mtx/responses/notifications.hpp>
 #include <mtx/responses/sync.hpp>
 #include <mtxclient/crypto/types.hpp>
@@ -24,7 +23,6 @@
 
 #include "CacheCryptoStructs.h"
 #include "CacheStructs.h"
-#include "Logging.h"
 
 namespace mtx::responses {
 struct Messages;
@@ -364,8 +362,6 @@ private:
                        mtx::events::collections::TimelineEvents e,
                        const std::string &room_id);
 
-    //! Remove a room from the cache.
-    // void removeLeftRoom(lmdb::txn &txn, const std::string &room_id);
     template<class T>
     void saveStateEvents(lmdb::txn &txn,
                          lmdb::dbi &statesdb,
@@ -373,11 +369,7 @@ private:
                          lmdb::dbi &membersdb,
                          lmdb::dbi &eventsDb,
                          const std::string &room_id,
-                         const std::vector<T> &events)
-    {
-        for (const auto &e : events)
-            saveStateEvent(txn, statesdb, stateskeydb, membersdb, eventsDb, room_id, e);
-    }
+                         const std::vector<T> &events);
 
     template<class T>
     void saveStateEvent(lmdb::txn &txn,
@@ -386,191 +378,18 @@ private:
                         lmdb::dbi &membersdb,
                         lmdb::dbi &eventsDb,
                         const std::string &room_id,
-                        const T &event)
-    {
-        using namespace mtx::events;
-        using namespace mtx::events::state;
-
-        if (auto e = std::get_if<StateEvent<Member>>(&event); e != nullptr) {
-            switch (e->content.membership) {
-            //
-            // We only keep users with invite or join membership.
-            //
-            case Membership::Invite:
-            case Membership::Join: {
-                auto display_name =
-                  e->content.display_name.empty() ? e->state_key : e->content.display_name;
-
-                std::string inviter = "";
-                if (e->content.membership == mtx::events::state::Membership::Invite) {
-                    inviter = e->sender;
-                }
-
-                // Lightweight representation of a member.
-                MemberInfo tmp{
-                  display_name,
-                  e->content.avatar_url,
-                  inviter,
-                  e->content.reason,
-                  e->content.is_direct,
-                };
-
-                membersdb.put(txn, e->state_key, nlohmann::json(tmp).dump());
-                break;
-            }
-            default: {
-                membersdb.del(txn, e->state_key, "");
-                break;
-            }
-            }
-        } else if (auto encr = std::get_if<StateEvent<Encryption>>(&event)) {
-            if (!encr->state_key.empty())
-                return;
-
-            setEncryptedRoom(txn, room_id);
-
-            std::string_view temp;
-            // ensure we don't replace the event in the db
-            if (statesdb.get(txn, to_string(encr->type), temp)) {
-                return;
-            }
-        }
-
-        std::visit(
-          [&txn, &statesdb, &stateskeydb, &eventsDb, &membersdb](const auto &e) {
-              if constexpr (isStateEvent_<decltype(e)>) {
-                  eventsDb.put(txn, e.event_id, nlohmann::json(e).dump());
-
-                  if (e.type != EventType::Unsupported) {
-                      if (std::is_same_v<std::remove_cv_t<std::remove_reference_t<decltype(e)>>,
-                                         StateEvent<mtx::events::msg::Redacted>>) {
-                          // apply the redaction event
-                          if (e.type == EventType::RoomMember) {
-                              // membership is not revoked, but names are yeeted (so we set the name
-                              // to the mxid)
-                              MemberInfo tmp{e.state_key, ""};
-                              membersdb.put(txn, e.state_key, nlohmann::json(tmp).dump());
-                          } else if (e.state_key.empty()) {
-                              // strictly speaking some stuff in those events can be redacted, but
-                              // this is close enough. Ref:
-                              // https://spec.matrix.org/v1.6/rooms/v10/#redactions
-                              if (e.type != EventType::RoomCreate &&
-                                  e.type != EventType::RoomJoinRules &&
-                                  e.type != EventType::RoomPowerLevels &&
-                                  e.type != EventType::RoomHistoryVisibility)
-                                  statesdb.del(txn, to_string(e.type));
-                          } else
-                              stateskeydb.del(
-                                txn, to_string(e.type), e.state_key + '\0' + e.event_id);
-                      } else if (e.state_key.empty()) {
-                          statesdb.put(txn, to_string(e.type), nlohmann::json(e).dump());
-                      } else {
-                          auto data = e.state_key + '\0' + e.event_id;
-                          auto key  = to_string(e.type);
-
-                          // Work around https://bugs.openldap.org/show_bug.cgi?id=8447
-                          stateskeydb.del(txn, key, data);
-                          stateskeydb.put(txn, key, data);
-                      }
-                  }
-              }
-          },
-          event);
-    }
+                        const T &event);
 
     template<typename T>
     std::optional<mtx::events::StateEvent<T>>
-    getStateEvent(lmdb::txn &txn, const std::string &room_id, std::string_view state_key = "")
-    {
-        try {
-            constexpr auto type = mtx::events::state_content_to_type<T>;
-            static_assert(type != mtx::events::EventType::Unsupported,
-                          "Not a supported type in state events.");
-
-            if (room_id.empty())
-                return std::nullopt;
-            const auto typeStr = to_string(type);
-
-            std::string_view value;
-            if (state_key.empty()) {
-                auto db = getStatesDb(txn, room_id);
-                if (!db.get(txn, typeStr, value)) {
-                    return std::nullopt;
-                }
-            } else {
-                auto db = getStatesKeyDb(txn, room_id);
-                // we can search using state key, since the compare functions defaults to the whole
-                // string, when there is no nullbyte
-                std::string_view data     = state_key;
-                std::string_view typeStrV = typeStr;
-
-                auto cursor = lmdb::cursor::open(txn, db);
-                if (!cursor.get(typeStrV, data, MDB_GET_BOTH))
-                    return std::nullopt;
-
-                try {
-                    auto eventsDb = getEventsDb(txn, room_id);
-                    auto eventid  = data;
-                    if (auto sep = data.rfind('\0'); sep != std::string_view::npos) {
-                        if (!eventsDb.get(txn, eventid.substr(sep + 1), value))
-                            return std::nullopt;
-                    } else {
-                        return std::nullopt;
-                    }
-
-                } catch (std::exception &) {
-                    return std::nullopt;
-                }
-            }
-
-            return nlohmann::json::parse(value).get<mtx::events::StateEvent<T>>();
-        } catch (std::exception &) {
-            return std::nullopt;
-        }
-    }
+    getStateEvent(lmdb::txn &txn, const std::string &room_id, std::string_view state_key = "");
 
     template<typename T>
     std::vector<mtx::events::StateEvent<T>>
     getStateEventsWithType(lmdb::txn &txn,
                            const std::string &room_id,
-                           mtx::events::EventType type = mtx::events::state_content_to_type<T>)
+                           mtx::events::EventType type = mtx::events::state_content_to_type<T>);
 
-    {
-        if (room_id.empty())
-            return {};
-
-        std::vector<mtx::events::StateEvent<T>> events;
-
-        {
-            auto db                   = getStatesKeyDb(txn, room_id);
-            auto eventsDb             = getEventsDb(txn, room_id);
-            const auto typeStr        = to_string(type);
-            std::string_view typeStrV = typeStr;
-            std::string_view data;
-            std::string_view value;
-
-            auto cursor = lmdb::cursor::open(txn, db);
-            bool first  = true;
-            if (cursor.get(typeStrV, data, MDB_SET)) {
-                while (cursor.get(typeStrV, data, first ? MDB_FIRST_DUP : MDB_NEXT_DUP)) {
-                    first = false;
-
-                    try {
-                        auto eventid = data;
-                        if (auto sep = data.rfind('\0'); sep != std::string_view::npos) {
-                            if (eventsDb.get(txn, eventid.substr(sep + 1), value))
-                                events.push_back(
-                                  nlohmann::json::parse(value).get<mtx::events::StateEvent<T>>());
-                        }
-                    } catch (std::exception &e) {
-                        nhlog::db()->warn("Failed to parse state event: {}", e.what());
-                    }
-                }
-            }
-        }
-
-        return events;
-    }
     void
     saveInvites(lmdb::txn &txn, const std::map<std::string, mtx::responses::InvitedRoom> &rooms);
 
@@ -723,3 +542,33 @@ namespace cache {
 Cache *
 client();
 }
+
+#define NHEKO_CACHE_GET_STATE_EVENT_FORWARD(Content)                                               \
+    extern template std::optional<mtx::events::StateEvent<Content>> Cache::getStateEvent(          \
+      lmdb::txn &txn, const std::string &room_id, std::string_view state_key);                     \
+                                                                                                   \
+    extern template std::vector<mtx::events::StateEvent<Content>> Cache::getStateEventsWithType(   \
+      lmdb::txn &txn, const std::string &room_id, mtx::events::EventType type);
+
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Aliases)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Avatar)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::CanonicalAlias)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Create)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Encryption)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::GuestAccess)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::HistoryVisibility)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::JoinRules)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Member)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Name)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::PinnedEvents)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::PowerLevels)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Tombstone)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::ServerAcl)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Topic)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::Widget)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::policy_rule::UserRule)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::policy_rule::RoomRule)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::policy_rule::ServerRule)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::space::Child)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::state::space::Parent)
+NHEKO_CACHE_GET_STATE_EVENT_FORWARD(mtx::events::msc2545::ImagePack)
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index db5cbbe8..03144faf 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -9,6 +9,8 @@
 #include <algorithm>
 #include <unordered_set>
 
+#include <nlohmann/json.hpp>
+
 #include <mtx/responses.hpp>
 
 #include "AvatarProvider.h"
@@ -282,8 +284,8 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QObject *parent)
                             continue;
 
                         mtx::events::collections::TimelineEvents te{event};
-                        std::visit([room_id = room_id](auto &event_) { event_.room_id = room_id; },
-                                   te);
+                        std::visit(
+                          [room_id_ = room_id](auto &event_) { event_.room_id = room_id_; }, te);
 
                         if (auto encryptedEvent =
                               std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
@@ -342,14 +344,14 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QObject *parent)
                       roomModel->roomAvatarUrl(),
                       96,
                       this,
-                      [this, te = te, room_id = room_id, actions = actions](QPixmap image) {
+                      [this, te_ = te, room_id_ = room_id, actions_ = actions](QPixmap image) {
                           notificationsManager->postNotification(
                             mtx::responses::Notification{
-                              .actions     = actions,
-                              .event       = std::move(te),
+                              .actions     = actions_,
+                              .event       = std::move(te_),
                               .read        = false,
                               .profile_tag = "",
-                              .room_id     = room_id,
+                              .room_id     = room_id_,
                               .ts          = 0,
                             },
                             image.toImage());
diff --git a/src/CompletionProxyModel.cpp b/src/CompletionProxyModel.cpp
index eea1e6aa..bce873e7 100644
--- a/src/CompletionProxyModel.cpp
+++ b/src/CompletionProxyModel.cpp
@@ -9,7 +9,6 @@
 
 #include "CompletionModelRoles.h"
 #include "Logging.h"
-#include "Utils.h"
 
 CompletionProxyModel::CompletionProxyModel(QAbstractItemModel *model,
                                            int max_mistakes,
diff --git a/src/EventAccessors.cpp b/src/EventAccessors.cpp
index 62969ed1..a8ba9e22 100644
--- a/src/EventAccessors.cpp
+++ b/src/EventAccessors.cpp
@@ -8,7 +8,6 @@
 
 #include <algorithm>
 #include <cctype>
-#include <concepts>
 #include <type_traits>
 
 namespace {
diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp
index 76f37fad..ecb9e66c 100644
--- a/src/InviteesModel.cpp
+++ b/src/InviteesModel.cpp
@@ -4,7 +4,6 @@
 
 #include "InviteesModel.h"
 
-#include "Cache.h"
 #include "Logging.h"
 #include "MatrixClient.h"
 #include "mtx/responses/profile.hpp"
diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp
index ef083074..f34275c9 100644
--- a/src/JdenticonProvider.cpp
+++ b/src/JdenticonProvider.cpp
@@ -12,10 +12,7 @@
 
 #include <mtxclient/crypto/client.hpp>
 
-#include "Cache.h"
 #include "Logging.h"
-#include "MatrixClient.h"
-#include "Utils.h"
 #include "jdenticoninterface.h"
 
 namespace Jdenticon {
diff --git a/src/JdenticonProvider.h b/src/JdenticonProvider.h
index da4d73e1..1c2c0665 100644
--- a/src/JdenticonProvider.h
+++ b/src/JdenticonProvider.h
@@ -11,8 +11,6 @@
 
 #include <mtx/common.hpp>
 
-#include "jdenticoninterface.h"
-
 class JdenticonRunnable final
   : public QObject
   , public QRunnable
diff --git a/src/Logging.cpp b/src/Logging.cpp
index e7a99458..35fea1d4 100644
--- a/src/Logging.cpp
+++ b/src/Logging.cpp
@@ -9,7 +9,6 @@
 #include "spdlog/sinks/rotating_file_sink.h"
 #include "spdlog/sinks/stdout_color_sinks.h"
 #include "spdlog/spdlog.h"
-#include <iostream>
 
 #include <QString>
 #include <QtGlobal>
diff --git a/src/Logging.h b/src/Logging.h
index 9b992660..56f4de8f 100644
--- a/src/Logging.h
+++ b/src/Logging.h
@@ -5,7 +5,6 @@
 #pragma once
 
 #include <memory>
-#include <string>
 
 #include <QString>
 
diff --git a/src/LoginPage.cpp b/src/LoginPage.cpp
index afc660a8..c4e9319c 100644
--- a/src/LoginPage.cpp
+++ b/src/LoginPage.cpp
@@ -11,7 +11,6 @@
 #include <mtx/responses/login.hpp>
 #include <mtx/responses/version.hpp>
 
-#include "Config.h"
 #include "Logging.h"
 #include "LoginPage.h"
 #include "MainWindow.h"
diff --git a/src/MainWindow.h b/src/MainWindow.h
index fc06e183..c493b5b2 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -14,8 +14,6 @@
 #include "UserSettingsPage.h"
 #include "dock/Dock.h"
 
-#include "jdenticoninterface.h"
-
 class ChatPage;
 class RegisterPage;
 class WelcomePage;
diff --git a/src/MatrixClient.cpp b/src/MatrixClient.cpp
index 2fd2eac9..05ec6a92 100644
--- a/src/MatrixClient.cpp
+++ b/src/MatrixClient.cpp
@@ -5,14 +5,12 @@
 #include "MatrixClient.h"
 
 #include <memory>
-#include <set>
 
 #include <QMetaType>
 #include <QObject>
 #include <QStandardPaths>
 #include <QString>
 
-#include "nlohmann/json.hpp"
 #include <mtx/responses.hpp>
 
 namespace http {
diff --git a/src/MemberList.cpp b/src/MemberList.cpp
index 036ef9d4..a5e6565e 100644
--- a/src/MemberList.cpp
+++ b/src/MemberList.cpp
@@ -7,9 +7,7 @@
 #include "Cache.h"
 #include "Cache_p.h"
 #include "ChatPage.h"
-#include "Config.h"
 #include "Logging.h"
-#include "Utils.h"
 #include "timeline/TimelineViewManager.h"
 
 MemberListBackend::MemberListBackend(const QString &room_id, QObject *parent)
diff --git a/src/PowerlevelsEditModels.h b/src/PowerlevelsEditModels.h
index c9d262d8..6e17079c 100644
--- a/src/PowerlevelsEditModels.h
+++ b/src/PowerlevelsEditModels.h
@@ -8,10 +8,9 @@
 #include <QQmlEngine>
 #include <QSortFilterProxyModel>
 
+#include <mtx/events/event_type.hpp>
 #include <mtx/events/power_levels.hpp>
 
-#include "CacheStructs.h"
-
 class PowerlevelsTypeListModel final : public QAbstractListModel
 {
     Q_OBJECT
diff --git a/src/RegisterPage.cpp b/src/RegisterPage.cpp
index 3b1ba658..2c0a5b07 100644
--- a/src/RegisterPage.cpp
+++ b/src/RegisterPage.cpp
@@ -10,7 +10,6 @@
 #include <mtx/responses/well-known.hpp>
 #include <mtxclient/http/client.hpp>
 
-#include "Config.h"
 #include "Logging.h"
 #include "LoginPage.h"
 #include "MainWindow.h"
diff --git a/src/RoomsModel.cpp b/src/RoomsModel.cpp
index 32dce4f6..7481eb51 100644
--- a/src/RoomsModel.cpp
+++ b/src/RoomsModel.cpp
@@ -6,7 +6,6 @@
 
 #include <QUrl>
 
-#include "Cache.h"
 #include "Cache_p.h"
 #include "CompletionModelRoles.h"
 #include "UserSettingsPage.h"
diff --git a/src/SingleImagePackModel.cpp b/src/SingleImagePackModel.cpp
index 686184da..1adfdaa2 100644
--- a/src/SingleImagePackModel.cpp
+++ b/src/SingleImagePackModel.cpp
@@ -8,6 +8,8 @@
 #include <QFileInfo>
 #include <QMimeDatabase>
 
+#include <nlohmann/json.hpp>
+
 #include <unordered_set>
 
 #include <mtx/responses/media.hpp>
diff --git a/src/UserDirectoryModel.cpp b/src/UserDirectoryModel.cpp
index ae8e19b1..72a5a13e 100644
--- a/src/UserDirectoryModel.cpp
+++ b/src/UserDirectoryModel.cpp
@@ -8,7 +8,6 @@
 
 #include <mtx/responses/users.hpp>
 
-#include "Cache.h"
 #include "Logging.h"
 #include "MatrixClient.h"
 
diff --git a/src/UsersModel.cpp b/src/UsersModel.cpp
index 453ef137..a017aa84 100644
--- a/src/UsersModel.cpp
+++ b/src/UsersModel.cpp
@@ -9,6 +9,7 @@
 #include "Cache.h"
 #include "Cache_p.h"
 #include "CompletionModelRoles.h"
+#include "Logging.h"
 #include "UserSettingsPage.h"
 
 UsersModel::UsersModel(const std::string &roomId, QObject *parent)
diff --git a/src/Utils.cpp b/src/Utils.cpp
index bb4df7d3..7a487101 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -4,6 +4,11 @@
 
 #include "Utils.h"
 
+#include <array>
+#include <cmath>
+#include <unordered_set>
+#include <variant>
+
 #include <QApplication>
 #include <QBuffer>
 #include <QComboBox>
@@ -20,14 +25,12 @@
 #include <QWindow>
 #include <QXmlStreamReader>
 
-#include <array>
-#include <cmath>
-#include <mtx/responses/messages.hpp>
-#include <unordered_set>
-#include <variant>
+#include <nlohmann/json.hpp>
 
 #include <cmark.h>
 
+#include <mtx/responses/messages.hpp>
+
 #include "Cache.h"
 #include "Cache_p.h"
 #include "ChatPage.h"
@@ -36,7 +39,6 @@
 #include "Logging.h"
 #include "MatrixClient.h"
 #include "UserSettingsPage.h"
-#include "timeline/Permissions.h"
 
 template<class T, class Event>
 static DescInfo
diff --git a/src/Utils.h b/src/Utils.h
index 83f2cad1..2c75cb5c 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -4,8 +4,6 @@
 
 #pragma once
 
-#include <variant>
-
 #include <CacheStructs.h>
 #include <QCoreApplication>
 #include <QDateTime>
diff --git a/src/dbus/NhekoDBusBackend.cpp b/src/dbus/NhekoDBusBackend.cpp
index 9690cdfa..5e0f8418 100644
--- a/src/dbus/NhekoDBusBackend.cpp
+++ b/src/dbus/NhekoDBusBackend.cpp
@@ -6,7 +6,6 @@
 
 #include <mutex>
 
-#include "Cache.h"
 #include "Cache_p.h"
 #include "ChatPage.h"
 #include "Logging.h"
diff --git a/src/encryption/DeviceVerificationFlow.cpp b/src/encryption/DeviceVerificationFlow.cpp
index 1e7ed7bc..f0c7ed52 100644
--- a/src/encryption/DeviceVerificationFlow.cpp
+++ b/src/encryption/DeviceVerificationFlow.cpp
@@ -4,6 +4,13 @@
 
 #include "DeviceVerificationFlow.h"
 
+#include <tuple>
+
+#include <QDateTime>
+#include <QTimer>
+
+#include <nlohmann/json.hpp>
+
 #include "Cache.h"
 #include "Cache_p.h"
 #include "ChatPage.h"
@@ -11,11 +18,6 @@
 #include "Utils.h"
 #include "timeline/TimelineModel.h"
 
-#include <QDateTime>
-#include <QTimer>
-#include <iostream>
-#include <tuple>
-
 static constexpr int TIMEOUT = 2 * 60 * 1000; // 2 minutes
 
 static mtx::events::msg::KeyVerificationMac
diff --git a/src/encryption/DeviceVerificationFlow.h b/src/encryption/DeviceVerificationFlow.h
index 50818883..6b1776e0 100644
--- a/src/encryption/DeviceVerificationFlow.h
+++ b/src/encryption/DeviceVerificationFlow.h
@@ -11,7 +11,6 @@
 #include "CacheCryptoStructs.h"
 #include "Logging.h"
 #include "MatrixClient.h"
-#include "Olm.h"
 #include "timeline/TimelineModel.h"
 
 class QTimer;
diff --git a/src/encryption/Olm.cpp b/src/encryption/Olm.cpp
index 7fa176b0..aaa7e43f 100644
--- a/src/encryption/Olm.cpp
+++ b/src/encryption/Olm.cpp
@@ -22,7 +22,6 @@
 #include "Logging.h"
 #include "MatrixClient.h"
 #include "UserSettingsPage.h"
-#include "Utils.h"
 
 namespace {
 auto client_ = std::make_unique<mtx::crypto::OlmClient>();
diff --git a/src/encryption/Olm.h b/src/encryption/Olm.h
index 726b9590..252d08b4 100644
--- a/src/encryption/Olm.h
+++ b/src/encryption/Olm.h
@@ -4,7 +4,6 @@
 
 #pragma once
 
-#include <memory>
 #include <mtx/events.hpp>
 #include <mtx/events/encrypted.hpp>
 #include <mtxclient/crypto/client.hpp>
diff --git a/src/encryption/SelfVerificationStatus.cpp b/src/encryption/SelfVerificationStatus.cpp
index e54cbc2d..8981244d 100644
--- a/src/encryption/SelfVerificationStatus.cpp
+++ b/src/encryption/SelfVerificationStatus.cpp
@@ -6,6 +6,10 @@
 
 #include <QApplication>
 
+#include <nlohmann/json.hpp>
+
+#include <mtx/responses/common.hpp>
+
 #include "Cache.h"
 #include "Cache_p.h"
 #include "ChatPage.h"
@@ -16,8 +20,6 @@
 #include "timeline/TimelineViewManager.h"
 #include "ui/UIA.h"
 
-#include <mtx/responses/common.hpp>
-
 SelfVerificationStatus::SelfVerificationStatus(QObject *o)
   : QObject(o)
 {
diff --git a/src/notifications/Manager.cpp b/src/notifications/Manager.cpp
index ed5c0670..5790d43e 100644
--- a/src/notifications/Manager.cpp
+++ b/src/notifications/Manager.cpp
@@ -6,7 +6,6 @@
 
 #include "Cache.h"
 #include "EventAccessors.h"
-#include "Logging.h"
 #include "Utils.h"
 
 QString
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index c47a3426..04e548f0 100644
--- a/src/timeline/EventStore.cpp
+++ b/src/timeline/EventStore.cpp
@@ -7,6 +7,8 @@
 #include <QThread>
 #include <QTimer>
 
+#include <nlohmann/json.hpp>
+
 #include <mtx/responses/common.hpp>
 
 #include "Cache.h"
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index fcec8e9c..bcb30aa0 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -14,20 +14,20 @@
 #include <QMediaPlayer>
 #include <QMimeData>
 #include <QMimeDatabase>
+#include <QRegularExpression>
 #include <QStandardPaths>
 #include <QTextBoundaryFinder>
 #include <QVideoFrame>
 #include <QVideoSink>
 
-#include <QRegularExpression>
+#include <nlohmann/json.hpp>
+
 #include <mtx/responses/common.hpp>
 #include <mtx/responses/media.hpp>
 
 #include "Cache.h"
 #include "Cache_p.h"
 #include "ChatPage.h"
-#include "CombinedImagePackModel.h"
-#include "Config.h"
 #include "EventAccessors.h"
 #include "Logging.h"
 #include "MatrixClient.h"
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index aefdc860..490641a6 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <thread>
 #include <type_traits>
+#include <utility>
 
 #include <QClipboard>
 #include <QDesktopServices>
@@ -17,7 +18,8 @@
 #include <QRegularExpression>
 #include <QStandardPaths>
 #include <QVariant>
-#include <utility>
+
+#include <nlohmann/json.hpp>
 
 #include "Cache.h"
 #include "Cache_p.h"
diff --git a/src/ui/MxcMediaProxy.h b/src/ui/MxcMediaProxy.h
index d245dcae..64c61c4f 100644
--- a/src/ui/MxcMediaProxy.h
+++ b/src/ui/MxcMediaProxy.h
@@ -13,8 +13,6 @@
 #include <QUrl>
 #include <QVideoSink>
 
-#include "Logging.h"
-
 class TimelineModel;
 
 // I failed to get my own buffer into the MediaPlayer in qml, so just make our own. For that we just
diff --git a/src/ui/NhekoDropArea.cpp b/src/ui/NhekoDropArea.cpp
index 348ef5d8..571ccf69 100644
--- a/src/ui/NhekoDropArea.cpp
+++ b/src/ui/NhekoDropArea.cpp
@@ -11,8 +11,6 @@
 #include "timeline/TimelineModel.h"
 #include "timeline/TimelineViewManager.h"
 
-#include "Logging.h"
-
 NhekoDropArea::NhekoDropArea(QQuickItem *parent)
   : QQuickItem(parent)
 {
diff --git a/src/ui/RoomSettings.cpp b/src/ui/RoomSettings.cpp
index 5f4184b3..073b27d0 100644
--- a/src/ui/RoomSettings.cpp
+++ b/src/ui/RoomSettings.cpp
@@ -15,7 +15,6 @@
 
 #include "Cache.h"
 #include "Cache_p.h"
-#include "Config.h"
 #include "Logging.h"
 #include "MatrixClient.h"
 #include "Utils.h"