| 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 | |