1 // SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
2 // SPDX-FileCopyrightText: 2021 Nheko Contributors
3 //
4 // SPDX-License-Identifier: GPL-3.0-or-later
5 
6 #pragma once
7 
8 #include <atomic>
9 #include <optional>
10 #include <stack>
11 #include <variant>
12 
13 #include <mtx/common.hpp>
14 #include <mtx/events.hpp>
15 #include <mtx/events/encrypted.hpp>
16 #include <mtx/events/member.hpp>
17 #include <mtx/events/presence.hpp>
18 #include <mtx/secret_storage.hpp>
19 
20 #include <QHBoxLayout>
21 #include <QMap>
22 #include <QPoint>
23 #include <QTimer>
24 #include <QWidget>
25 
26 #include "CacheCryptoStructs.h"
27 #include "CacheStructs.h"
28 #include "notifications/Manager.h"
29 
30 class OverlayModal;
31 class TimelineViewManager;
32 class UserSettings;
33 class NotificationsManager;
34 class TimelineModel;
35 class CallManager;
36 
37 constexpr int CONSENSUS_TIMEOUT      = 1000;
38 constexpr int SHOW_CONTENT_TIMEOUT   = 3000;
39 constexpr int TYPING_REFRESH_TIMEOUT = 10000;
40 
41 namespace mtx::requests {
42 struct CreateRoom;
43 }
44 namespace mtx::responses {
45 struct Notifications;
46 struct Sync;
47 struct Timeline;
48 struct Rooms;
49 }
50 
51 using SecretsToDecrypt = std::map<std::string, mtx::secret_storage::AesHmacSha2EncryptedData>;
52 
53 class ChatPage : public QWidget
54 {
55     Q_OBJECT
56 
57 public:
58     ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent = nullptr);
59 
60     // Initialize all the components of the UI.
61     void bootstrap(QString userid, QString homeserver, QString token);
62 
instance()63     static ChatPage *instance() { return instance_; }
64 
userSettings()65     QSharedPointer<UserSettings> userSettings() { return userSettings_; }
callManager()66     CallManager *callManager() { return callManager_; }
timelineManager()67     TimelineViewManager *timelineManager() { return view_manager_; }
68     void deleteConfigs();
69 
70     void initiateLogout();
71 
72     QString status() const;
73     void setStatus(const QString &status);
74 
75     mtx::presence::PresenceState currentPresence() const;
76 
77     // TODO(Nico): Get rid of this!
78     QString currentRoom() const;
79 
80 public slots:
81     bool handleMatrixUri(const QByteArray &uri);
82     bool handleMatrixUri(const QUrl &uri);
83 
84     void startChat(QString userid);
85     void leaveRoom(const QString &room_id);
86     void createRoom(const mtx::requests::CreateRoom &req);
87     void joinRoom(const QString &room);
88     void joinRoomVia(const std::string &room_id,
89                      const std::vector<std::string> &via,
90                      bool promptForConfirmation = true);
91 
92     void inviteUser(QString userid, QString reason);
93     void kickUser(QString userid, QString reason);
94     void banUser(QString userid, QString reason);
95     void unbanUser(QString userid, QString reason);
96 
97     void receivedSessionKey(const std::string &room_id, const std::string &session_id);
98     void decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc,
99                                   const SecretsToDecrypt &secrets);
100 signals:
101     void connectionLost();
102     void connectionRestored();
103 
104     void notificationsRetrieved(const mtx::responses::Notifications &);
105     void highlightedNotifsRetrieved(const mtx::responses::Notifications &, const QPoint widgetPos);
106 
107     void contentLoaded();
108     void closing();
109     void changeWindowTitle(const int);
110     void unreadMessages(int count);
111     void showNotification(const QString &msg);
112     void showLoginPage(const QString &msg);
113     void showUserSettingsPage();
114     void showOverlayProgressBar();
115 
116     void ownProfileOk();
117     void setUserDisplayName(const QString &name);
118     void setUserAvatar(const QString &avatar);
119     void loggedOut();
120 
121     void trySyncCb();
122     void tryDelayedSyncCb();
123     void tryInitialSyncCb();
124     void newSyncResponse(const mtx::responses::Sync &res, const std::string &prev_batch_token);
125     void leftRoom(const QString &room_id);
126     void newRoom(const QString &room_id);
127     void changeToRoom(const QString &room_id);
128 
129     void initializeViews(const mtx::responses::Rooms &rooms);
130     void initializeEmptyViews();
131     void initializeMentions(const QMap<QString, mtx::responses::Notifications> &notifs);
132     void syncUI(const mtx::responses::Rooms &rooms);
133     void dropToLoginPageCb(const QString &msg);
134 
135     void notifyMessage(const QString &roomid,
136                        const QString &eventid,
137                        const QString &roomname,
138                        const QString &sender,
139                        const QString &message,
140                        const QImage &icon);
141 
142     void retrievedPresence(const QString &statusMsg, mtx::presence::PresenceState state);
143     void themeChanged();
144     void decryptSidebarChanged();
145     void chatFocusChanged(const bool focused);
146 
147     //! Signals for device verificaiton
148     void receivedDeviceVerificationAccept(const mtx::events::msg::KeyVerificationAccept &message);
149     void receivedDeviceVerificationRequest(const mtx::events::msg::KeyVerificationRequest &message,
150                                            std::string sender);
151     void receivedRoomDeviceVerificationRequest(
152       const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &message,
153       TimelineModel *model);
154     void receivedDeviceVerificationCancel(const mtx::events::msg::KeyVerificationCancel &message);
155     void receivedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message);
156     void receivedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message);
157     void receivedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message,
158                                          std::string sender);
159     void receivedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message);
160     void receivedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message);
161 
162     void downloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc,
163                            const SecretsToDecrypt &secrets);
164 
165 private slots:
166     void logout();
167     void removeRoom(const QString &room_id);
168     void changeRoom(const QString &room_id);
169     void dropToLoginPage(const QString &msg);
170 
171     void handleSyncResponse(const mtx::responses::Sync &res, const std::string &prev_batch_token);
172 
173 private:
174     static ChatPage *instance_;
175 
176     void startInitialSync();
177     void tryInitialSync();
178     void trySync();
179     void verifyOneTimeKeyCountAfterStartup();
180     void ensureOneTimeKeyCount(const std::map<std::string, uint16_t> &counts);
181     void getProfileInfo();
182     void getBackupVersion();
183 
184     //! Check if the given room is currently open.
185     bool isRoomActive(const QString &room_id);
186 
187     using UserID      = QString;
188     using Membership  = mtx::events::StateEvent<mtx::events::state::Member>;
189     using Memberships = std::map<std::string, Membership>;
190 
191     void loadStateFromCache();
192     void resetUI();
193 
194     template<class Collection>
195     Memberships getMemberships(const std::vector<Collection> &events) const;
196 
197     //! Send desktop notification for the received messages.
198     void sendNotifications(const mtx::responses::Notifications &);
199 
200     template<typename T>
201     void connectCallMessage();
202 
203     QHBoxLayout *topLayout_;
204 
205     TimelineViewManager *view_manager_;
206 
207     QTimer connectivityTimer_;
208     std::atomic_bool isConnected_;
209 
210     // Global user settings.
211     QSharedPointer<UserSettings> userSettings_;
212 
213     NotificationsManager notificationsManager;
214     CallManager *callManager_;
215 };
216 
217 template<class Collection>
218 std::map<std::string, mtx::events::StateEvent<mtx::events::state::Member>>
getMemberships(const std::vector<Collection> & collection)219 ChatPage::getMemberships(const std::vector<Collection> &collection) const
220 {
221     std::map<std::string, mtx::events::StateEvent<mtx::events::state::Member>> memberships;
222 
223     using Member = mtx::events::StateEvent<mtx::events::state::Member>;
224 
225     for (const auto &event : collection) {
226         if (auto member = std::get_if<Member>(event)) {
227             memberships.emplace(member->state_key, *member);
228         }
229     }
230 
231     return memberships;
232 }
233