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 | |
25 | namespace Quotient { |
26 | |
27 | class EncryptedEvent; |
28 | |
29 | class Q_DECL_HIDDEN Quotient::Connection::Private { |
30 | public: |
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 | |