diff options
author | n-peugnet <n.peugnet@free.fr> | 2022-10-06 19:27:24 +0200 |
---|---|---|
committer | n-peugnet <n.peugnet@free.fr> | 2022-10-06 19:27:24 +0200 |
commit | 08632625e1a04257b5c7d4a9db2246ac07436748 (patch) | |
tree | 9ddadf219a7e352ddd3549ad1683282c944adfb6 /lib/connection.h | |
parent | e9c2e2a26d3711e755aa5eb8a8478917c71d612b (diff) | |
parent | d911b207f49e936b3e992200796110f0749ed150 (diff) | |
download | libquotient-08632625e1a04257b5c7d4a9db2246ac07436748.tar.gz libquotient-08632625e1a04257b5c7d4a9db2246ac07436748.zip |
Update upstream source from tag 'upstream/0.7.0'
Update to upstream version '0.7.0'
with Debian dir 30dcb77a77433e4a54eab77c0b82ae925dead2d8
Diffstat (limited to 'lib/connection.h')
-rw-r--r-- | lib/connection.h | 275 |
1 files changed, 152 insertions, 123 deletions
diff --git a/lib/connection.h b/lib/connection.h index c90cb892..75faf370 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -1,30 +1,16 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ +// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net> +// SPDX-FileCopyrightText: 2017 Roman Plášil <me@rplasil.name> +// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru> +// SPDX-License-Identifier: LGPL-2.1-or-later #pragma once -#include "ssosession.h" -#include "joinstate.h" -#include "qt_connection_util.h" #include "quotient_common.h" +#include "ssosession.h" +#include "util.h" -#include "csapi/login.h" #include "csapi/create_room.h" +#include "csapi/login.h" #include "events/accountdataevents.h" @@ -35,9 +21,12 @@ #include <functional> -namespace QtOlm { -class Account; -} +#ifdef Quotient_E2EE_ENABLED +#include "e2ee/e2ee.h" +#include "e2ee/qolmoutboundsession.h" +#include "keyverificationsession.h" +#include "events/keyverificationevent.h" +#endif Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow) @@ -61,29 +50,33 @@ class DownloadFileJob; class SendToDeviceJob; class SendMessageJob; class LeaveRoomJob; +class Database; +struct EncryptedFileMetadata; + +class QOlmAccount; +class QOlmInboundGroupSession; + +using LoginFlow = GetLoginFlowsJob::LoginFlow; + +/// Predefined login flows +namespace LoginFlows { + inline const LoginFlow Password { "m.login.password" }; + inline const LoginFlow SSO { "m.login.sso" }; + inline const LoginFlow Token { "m.login.token" }; +} // To simplify comparisons of LoginFlows -inline bool operator==(const GetLoginFlowsJob::LoginFlow& lhs, - const GetLoginFlowsJob::LoginFlow& rhs) +inline bool operator==(const LoginFlow& lhs, const LoginFlow& rhs) { return lhs.type == rhs.type; } -inline bool operator!=(const GetLoginFlowsJob::LoginFlow& lhs, - const GetLoginFlowsJob::LoginFlow& rhs) +inline bool operator!=(const LoginFlow& lhs, const LoginFlow& rhs) { return !(lhs == rhs); } -/// Predefined login flows -struct LoginFlows { - using LoginFlow = GetLoginFlowsJob::LoginFlow; - static inline const LoginFlow Password { "m.login.password" }; - static inline const LoginFlow SSO { "m.login.sso" }; - static inline const LoginFlow Token { "m.login.token" }; -}; - class Connection; using room_factory_t = @@ -96,11 +89,9 @@ using user_factory_t = std::function<User*(Connection*, const QString&)>; * \sa Connection::setRoomFactory, Connection::setRoomType */ template <typename T = Room> -static inline room_factory_t defaultRoomFactory() +auto defaultRoomFactory(Connection* c, const QString& id, JoinState js) { - return [](Connection* c, const QString& id, JoinState js) { - return new T(c, id, js); - }; + return new T(c, id, js); } /** The default factory to create user objects @@ -109,9 +100,9 @@ static inline room_factory_t defaultRoomFactory() * \sa Connection::setUserFactory, Connection::setUserType */ template <typename T = User> -static inline user_factory_t defaultUserFactory() +auto defaultUserFactory(Connection* c, const QString& id) { - return [](Connection* c, const QString& id) { return new T(id, c); }; + return new T(id, c); } // Room ids, rather than room pointers, are used in the direct chat @@ -120,9 +111,9 @@ static inline user_factory_t defaultUserFactory() // are stored with no regard to their state. using DirectChatsMap = QMultiHash<const User*, QString>; using DirectChatUsersMap = QMultiHash<QString, User*>; -using IgnoredUsersList = IgnoredUsersEvent::content_type; +using IgnoredUsersList = IgnoredUsersEvent::value_type; -class Connection : public QObject { +class QUOTIENT_API Connection : public QObject { Q_OBJECT Q_PROPERTY(User* localUser READ user NOTIFY stateChanged) @@ -139,10 +130,10 @@ class Connection : public QObject { Q_PROPERTY(bool supportsPasswordAuth READ supportsPasswordAuth NOTIFY loginFlowsChanged STORED false) Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY lazyLoadingChanged) + Q_PROPERTY(bool canChangePassword READ canChangePassword NOTIFY capabilitiesLoaded) public: - using UsersToDevicesToEvents = - UnorderedMap<QString, UnorderedMap<QString, const Event&>>; + using UsersToDevicesToContent = QHash<QString, QHash<QString, QJsonObject>>; enum RoomVisibility { PublishRoom, @@ -153,15 +144,6 @@ public: explicit Connection(const QUrl& server, QObject* parent = nullptr); ~Connection() override; - /// Get all Invited and Joined rooms - /*! - * \return a hashmap from a composite key - room name and whether - * it's an Invite rather than Join - to room pointers - * \sa allRooms, rooms, roomsWithTag - */ - [[deprecated("Use allRooms(), roomsWithTag() or rooms(joinStates) instead")]] - QHash<QPair<QString, bool>, Room*> roomMap() const; - /// Get all rooms known within this Connection /*! * This includes Invite, Join and Leave rooms, in no particular order. @@ -192,24 +174,25 @@ public: */ bool hasAccountData(const QString& type) const; - /** Get a generic account data event of the given type - * This returns an account data event of the given type - * stored on the server. Direct chats map cannot be retrieved - * using this method _yet_; use directChats() instead. - */ + //! \brief Get a generic account data event of the given type + //! + //! \return an account data event of the given type stored on the server, + //! or nullptr if there's none of that type. + //! \note Direct chats map cannot be retrieved using this method _yet_; + //! use directChats() instead. const EventPtr& accountData(const QString& type) const; - /** Get a generic account data event of the given type - * This returns an account data event of the given type - * stored on the server. Direct chats map cannot be retrieved - * using this method _yet_; use directChats() instead. - */ - template <typename EventT> - const typename EventT::content_type accountData() const + //! \brief Get an account data event of the given type + //! + //! \return the account data content for the given event type stored + //! on the server, or a default-constructed object if there's none + //! of that type. + //! \note Direct chats map cannot be retrieved using this method _yet_; + //! use directChats() instead. + template <EventClass EventT> + const EventT* accountData() const { - if (const auto& eventPtr = accountData(EventT::matrixTypeId())) - return eventPtr->content(); - return {}; + return eventCast<EventT>(accountData(EventT::TypeId)); } /** Get account data as a JSON object @@ -334,7 +317,38 @@ public: QByteArray accessToken() const; bool isLoggedIn() const; #ifdef Quotient_E2EE_ENABLED - QtOlm::Account* olmAccount() const; + QOlmAccount* olmAccount() const; + Database* database() const; + PicklingMode picklingMode() const; + + UnorderedMap<QString, QOlmInboundGroupSessionPtr> loadRoomMegolmSessions( + const Room* room) const; + void saveMegolmSession(const Room* room, + const QOlmInboundGroupSession& session) const; + QOlmOutboundGroupSessionPtr loadCurrentOutboundMegolmSession( + const QString& roomId) const; + void saveCurrentOutboundMegolmSession( + const QString& roomId, const QOlmOutboundGroupSession& session) const; + + QString edKeyForUserDevice(const QString& userId, + const QString& deviceId) const; + bool hasOlmSession(const QString& user, const QString& deviceId) const; + + // This assumes that an olm session already exists. If it doesn't, no message is sent. + void sendToDevice(const QString& targetUserId, const QString& targetDeviceId, + const Event& event, bool encrypted); + + /// Returns true if this megolm session comes from a verified device + bool isVerifiedSession(const QString& megolmSessionId) const; + + void sendSessionKeyToDevices(const QString& roomId, + const QByteArray& sessionId, + const QByteArray& sessionKey, + const QMultiHash<QString, QString>& devices, + int index); + + QJsonObject decryptNotification(const QJsonObject ¬ification); + QStringList devicesForUser(const QString& userId) const; #endif // Quotient_E2EE_ENABLED Q_INVOKABLE Quotient::SyncJob* syncJob() const; Q_INVOKABLE int millisToReconnect() const; @@ -368,22 +382,19 @@ public: * \sa loadingCapabilities */ QVector<SupportedRoomVersion> availableRoomVersions() const; + /// Indicate if the user can change its password from the client. + /// This is often not the case when SSO is enabled. + /// \sa loadingCapabilities + bool canChangePassword() const; + /** * Call this before first sync to load from previously saved file. - * - * \param fromFile A local path to read the state from. Uses QUrl - * to be QML-friendly. Empty parameter means saving to the directory - * defined by stateCachePath() / stateCacheDir(). */ Q_INVOKABLE void loadState(); /** * This method saves the current state of rooms (but not messages * in them) to a local cache file, so that it could be loaded by * loadState() on a next run of the client. - * - * \param toFile A local path to save the state to. Uses QUrl to be - * QML-friendly. Empty parameter means saving to the directory - * defined by stateCachePath() / stateCacheDir(). */ Q_INVOKABLE void saveState() const; @@ -418,7 +429,7 @@ public: /*! Start a pre-created job object on this connection */ Q_INVOKABLE BaseJob* run(BaseJob* job, - RunningPolicy runningPolicy = ForegroundRequest); + RunningPolicy runningPolicy = ForegroundRequest); /*! Start a job of a specified type with specified arguments and policy * @@ -463,6 +474,17 @@ public: std::forward<JobArgTs>(jobArgs)...); } + //! \brief Start a local HTTP server and generate a single sign-on URL + //! + //! This call does the preparatory steps to carry out single sign-on + //! sequence + //! \sa https://matrix.org/docs/guides/sso-for-client-developers + //! \return A proxy object holding two URLs: one for SSO on the chosen + //! homeserver and another for the local callback address. Normally + //! you won't need the callback URL unless you proxy the response + //! with a custom UI. You do not need to delete the SsoSession + //! object; the Connection that issued it will dispose of it once + //! the login sequence completes (with any outcome). Q_INVOKABLE SsoSession* prepareForSso(const QString& initialDeviceName, const QString& deviceId = {}); @@ -487,21 +509,37 @@ public: template <typename T> static void setRoomType() { - setRoomFactory(defaultRoomFactory<T>()); + setRoomFactory(defaultRoomFactory<T>); } /// Set the user factory to default with the overriden user type template <typename T> static void setUserType() { - setUserFactory(defaultUserFactory<T>()); + setUserFactory(defaultUserFactory<T>); } -public slots: - /** Set the homeserver base URL */ + /// Saves the olm account data to disk. Usually doesn't need to be called manually. + void saveOlmAccount(); + +public Q_SLOTS: + /// \brief Set the homeserver base URL and retrieve its login flows + /// + /// \sa LoginFlowsJob, loginFlows, loginFlowsChanged, homeserverChanged void setHomeserver(const QUrl& baseUrl); - /** Determine and set the homeserver from MXID */ + /// \brief Determine and set the homeserver from MXID + /// + /// This attempts to resolve the homeserver by requesting + /// .well-known/matrix/client record from the server taken from the MXID + /// serverpart. If there is no record found, the serverpart itself is + /// attempted as the homeserver base URL; if the record is there but + /// is malformed (e.g., the homeserver base URL cannot be found in it) + /// resolveError() is emitted and further processing stops. Otherwise, + /// setHomeserver is called, preparing the Connection object for the login + /// attempt. + /// \param mxid user Matrix ID, such as @someone:example.org + /// \sa setHomeserver, homeserverChanged, loginFlowsChanged, resolveError void resolveServer(const QString& mxid); /** \brief Log in using a username and password pair @@ -534,30 +572,12 @@ public slots: */ void assumeIdentity(const QString& mxId, const QString& accessToken, const QString& deviceId); - /*! \deprecated Use loginWithPassword instead */ - void connectToServer(const QString& userId, const QString& password, - const QString& initialDeviceName, - const QString& deviceId = {}) - { - loginWithPassword(userId, password, initialDeviceName, deviceId); - } - /*! \deprecated - * Use assumeIdentity() if you have an access token or - * loginWithToken() if you have a login token. - */ - void connectWithToken(const QString& userId, const QString& accessToken, - const QString& deviceId) - { - assumeIdentity(userId, accessToken, deviceId); - } /// Explicitly request capabilities from the server void reloadCapabilities(); /// Find out if capabilites are still loading from the server bool loadingCapabilities() const; - /** @deprecated Use stopSync() instead */ - void disconnectFromServer() { stopSync(); } void logout(); void sync(int timeout = -1); @@ -566,6 +586,8 @@ public slots: void stopSync(); QString nextBatchToken() const; + Q_INVOKABLE QUrl makeMediaUrl(QUrl mxcUrl) const; + virtual MediaThumbnailJob* getThumbnail(const QString& mediaId, QSize requestedSize, RunningPolicy policy = BackgroundRequest); @@ -587,6 +609,11 @@ public slots: DownloadFileJob* downloadFile(const QUrl& url, const QString& localFilename = {}); +#ifdef Quotient_E2EE_ENABLED + DownloadFileJob* downloadFile(const QUrl& url, + const EncryptedFileMetadata& fileMetadata, + const QString& localFilename = {}); +#endif /** * \brief Create a room (generic method) * This method allows to customize room entirely to your liking, @@ -664,7 +691,7 @@ public slots: ForgetRoomJob* forgetRoom(const QString& id); SendToDeviceJob* sendToDevices(const QString& eventType, - const UsersToDevicesToEvents& eventsMap); + const UsersToDevicesToContent& contents); /** \deprecated This method is experimental and may be removed any time */ SendMessageJob* sendMessage(const QString& roomId, const RoomEvent& event); @@ -672,24 +699,18 @@ public slots: /** \deprecated Do not use this directly, use Room::leaveRoom() instead */ virtual LeaveRoomJob* leaveRoom(Room* room); - // Old API that will be abolished any time soon. DO NOT USE. +#ifdef Quotient_E2EE_ENABLED + void startKeyVerificationSession(const QString& deviceId); - /** @deprecated Use callApi<PostReceiptJob>() or Room::postReceipt() instead - */ - virtual PostReceiptJob* postReceipt(Room* room, RoomEvent* event); + void encryptionUpdate(Room *room); +#endif -signals: - /** - * @deprecated - * This was a signal resulting from a successful resolveServer(). - * Since Connection now provides setHomeserver(), the HS URL - * may change even without resolveServer() invocation. Use - * loginFLowsChanged() instead of resolved(). You can also use - * loginWith*() and assumeIdentity() without the HS URL set in - * advance (i.e. without calling resolveServer), as they trigger - * server name resolution from MXID if the server URL is not valid. - */ - void resolved(); +Q_SIGNALS: + /// \brief Initial server resolution has failed + /// + /// This signal is emitted when resolveServer() did not manage to resolve + /// the homeserver using its .well-known/client record or otherwise. + /// \sa resolveServer void resolveError(QString error); void homeserverChanged(QUrl baseUrl); @@ -697,7 +718,6 @@ signals: void capabilitiesLoaded(); void connected(); - void reconnected(); //< \deprecated Use connected() instead void loggedOut(); /** Login data or state have changed * @@ -841,6 +861,15 @@ signals: void cacheStateChanged(); void lazyLoadingChanged(); void turnServersChanged(const QJsonObject& servers); + void devicesListLoaded(); + +#ifdef Quotient_E2EE_ENABLED + void newKeyVerificationSession(KeyVerificationSession* session); + void keyVerificationStateChanged( + const KeyVerificationSession* session, + Quotient::KeyVerificationSession::State state); + void sessionVerified(const QString& userId, const QString& deviceId); +#endif protected: /** @@ -875,12 +904,12 @@ protected: */ void onSyncSuccess(SyncData&& data, bool fromCache = false); -protected slots: +protected Q_SLOTS: void syncLoopIteration(); private: class Private; - QScopedPointer<Private> d; + ImplPtr<Private> d; static room_factory_t _roomFactory; static user_factory_t _userFactory; |