1// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
2// SPDX-FileCopyrightText: 2017 Roman Plášil <me@rplasil.name>
3// SPDX-FileCopyrightText: 2019 Ville Ranki <ville.ranki@iki.fi>
4// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
5// SPDX-License-Identifier: LGPL-2.1-or-later
6
7#include "connection.h"
8#include "connectiondata.h"
9#include "settings.h"
10#include "syncdata.h"
11
12#include "csapi/capabilities.h"
13#include "csapi/logout.h"
14#include "csapi/wellknown.h"
15
16#ifdef Quotient_E2EE_ENABLED
17# include "connectionencryptiondata_p.h"
18#endif
19
20#include "csapi/account-data.h"
21
22#include <QtCore/QCoreApplication>
23#include <QtCore/QPointer>
24
25namespace Quotient {
26
27class EncryptedEvent;
28
29class Q_DECL_HIDDEN Quotient::Connection::Private {
30public:
31 explicit Private(std::unique_ptr<ConnectionData>&& connection)
32 : data(std::move(connection))
33 {}
34
35 Connection* q = nullptr;
36 std::unique_ptr<ConnectionData> data;
37 // A complex key below is a pair of room name and whether its
38 // state is Invited. The spec mandates to keep Invited room state
39 // separately; specifically, we should keep objects for Invite and
40 // Leave state of the same room if the two happen to co-exist.
41 QHash<std::pair<QString, bool>, Room*> roomMap;
42 /// Mapping from serverparts to alias/room id mappings,
43 /// as of the last sync
44 QHash<QString, QString> roomAliasMap;
45 QVector<QString> roomIdsToForget;
46 QVector<QString> pendingStateRoomIds;
47 QMap<QString, User*> userMap;
48 DirectChatsMap directChats;
49 DirectChatUsersMap directChatUsers;
50 // The below two variables track local changes between sync completions.
51 // See https://github.com/quotient-im/libQuotient/wiki/Handling-direct-chat-events
52 DirectChatsMap dcLocalAdditions;
53 DirectChatsMap dcLocalRemovals;
54 UnorderedMap<QString, EventPtr> accountData;
55 QMetaObject::Connection syncLoopConnection {};
56 int syncTimeout = -1;
57
58 GetCapabilitiesJob* capabilitiesJob = nullptr;
59 GetCapabilitiesJob::Capabilities capabilities;
60
61 QVector<GetLoginFlowsJob::LoginFlow> loginFlows;
62
63#ifdef Quotient_E2EE_ENABLED
64 static inline bool encryptionDefault = false;
65 bool useEncryption = encryptionDefault;
66 std::unique_ptr<_impl::ConnectionEncryptionData> encryptionData;
67#else
68 static constexpr bool useEncryption = false;
69#endif
70
71 QPointer<GetWellknownJob> resolverJob = nullptr;
72 QPointer<GetLoginFlowsJob> loginFlowsJob = nullptr;
73
74 SyncJob* syncJob = nullptr;
75 QPointer<LogoutJob> logoutJob = nullptr;
76
77 bool cacheState = true;
78 bool cacheToBinary =
79 SettingsGroup("libQuotient"_ls).get(key: "cache_type"_ls,
80 defaultValue: SettingsGroup("libQMatrixClient"_ls).get<QString>(key: "cache_type"_ls))
81 != "json"_ls;
82 bool lazyLoading = false;
83
84 /** \brief Check the homeserver and resolve it if needed, before connecting
85 *
86 * A single entry for functions that need to check whether the homeserver
87 * is valid before running. May execute connectFn either synchronously
88 * or asynchronously. In case of errors, emits resolveError() if
89 * the homeserver URL is not valid and cannot be resolved from userId, or
90 * the homeserver doesn't support the requested login flow.
91 *
92 * \param userId fully-qualified MXID to resolve HS from
93 * \param connectFn a function to execute once the HS URL is good
94 * \param flow optionally, a login flow that should be supported for
95 * connectFn to work; `none`, if there's no login flow
96 * requirements
97 * \sa resolveServer, resolveError
98 */
99 void checkAndConnect(const QString &userId,
100 const std::function<void ()> &connectFn,
101 const std::optional<LoginFlow> &flow = none);
102 template <typename... LoginArgTs>
103 void loginToServer(LoginArgTs&&... loginArgs);
104 void completeSetup(const QString &mxId, bool mock = false);
105 void removeRoom(const QString& roomId);
106
107 void consumeRoomData(SyncDataList&& roomDataList, bool fromCache);
108 void consumeAccountData(Events&& accountDataEvents);
109 void consumePresenceData(Events&& presenceData);
110 void consumeToDeviceEvents(Events&& toDeviceEvents);
111
112 void packAndSendAccountData(EventPtr&& event)
113 {
114 const auto eventType = event->matrixType();
115 q->callApi<SetAccountDataJob>(jobArgs: data->userId(), jobArgs: eventType,
116 jobArgs: event->contentJson());
117 accountData[eventType] = std::move(event);
118 emit q->accountDataChanged(type: eventType);
119 }
120
121 template <EventClass EventT, typename ContentT>
122 void packAndSendAccountData(ContentT&& content)
123 {
124 packAndSendAccountData(
125 makeEvent<EventT>(std::forward<ContentT>(content)));
126 }
127 QString topLevelStatePath() const
128 {
129 return q->stateCacheDir().filePath(fileName: "state.json"_ls);
130 }
131
132 void saveAccessTokenToKeychain() const;
133 void dropAccessToken();
134};
135} // namespace Quotient
136