13 #include "csapi/create_room.h"
14 #include "csapi/login.h"
15 #include "csapi/content-repo.h"
17 #include "e2ee/qolmoutboundsession.h"
19 #include "events/accountdataevents.h"
20 #include "jobs/jobhandle.h"
22 #include <QtCore/QDir>
23 #include <QtCore/QObject>
24 #include <QtCore/QSize>
25 #include <QtCore/QUrl>
29 Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow)
31 class TestCrossSigning;
42 class GetCapabilitiesJob;
45 class RoomMessagesJob;
48 class MediaThumbnailJob;
50 class DownloadFileJob;
51 class SendToDeviceJob;
55 struct EncryptedFileMetadata;
58 class QOlmInboundGroupSession;
60 using LoginFlow = GetLoginFlowsJob::LoginFlow;
63 namespace LoginFlows {
64 inline const LoginFlow Password {
"m.login.password"_L1 };
65 inline const LoginFlow SSO {
"m.login.sso"_L1 };
66 inline const LoginFlow Token {
"m.login.token"_L1 };
71 inline bool operator==(
const LoginFlow& lhs,
const LoginFlow& rhs)
73 return lhs.type == rhs.type;
76 inline bool operator!=(
const LoginFlow& lhs,
const LoginFlow& rhs)
83 using room_factory_t =
84 std::function<Room*(Connection*,
const QString&, JoinState)>;
85 using user_factory_t = std::function<User*(Connection*,
const QString&)>;
91 template <
typename T = Room>
92 auto defaultRoomFactory(Connection* c,
const QString& id, JoinState js)
94 return new T(c, id, js);
101 template <
typename T = User>
102 auto defaultUserFactory(Connection* c,
const QString& id)
111 using DirectChatsMap = QMultiHash<
const User*, QString>;
112 using IgnoredUsersList = IgnoredUsersEvent::value_type;
117 Q_PROPERTY(User* localUser READ user NOTIFY stateChanged)
118 Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged)
119 Q_PROPERTY(QString domain READ domain NOTIFY stateChanged STORED
false)
120 Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged)
121 Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged)
122 Q_PROPERTY(
bool isLoggedIn READ isLoggedIn NOTIFY stateChanged STORED
false)
123 Q_PROPERTY(
bool isOnline READ isOnline NOTIFY isOnlineChanged)
124 Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY capabilitiesLoaded)
125 Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged)
126 Q_PROPERTY(QVector<GetLoginFlowsJob::LoginFlow> loginFlows READ loginFlows NOTIFY loginFlowsChanged)
127 Q_PROPERTY(
bool isUsable READ isUsable NOTIFY loginFlowsChanged STORED
false)
128 Q_PROPERTY(
bool supportsSso READ supportsSso NOTIFY loginFlowsChanged STORED
false)
129 Q_PROPERTY(
bool supportsPasswordAuth READ supportsPasswordAuth NOTIFY loginFlowsChanged STORED
false)
130 Q_PROPERTY(
bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged)
131 Q_PROPERTY(
bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY lazyLoadingChanged)
132 Q_PROPERTY(
bool canChangePassword READ canChangePassword NOTIFY capabilitiesLoaded)
133 Q_PROPERTY(
bool encryptionEnabled READ encryptionEnabled WRITE enableEncryption NOTIFY encryptionChanged)
134 Q_PROPERTY(
bool directChatEncryptionEnabled READ directChatEncryptionEnabled WRITE enableDirectChatEncryption NOTIFY directChatsEncryptionChanged)
135 Q_PROPERTY(QStringList accountDataEventTypes READ accountDataEventTypes NOTIFY accountDataChanged)
138 using UsersToDevicesToContent = QHash<QString, QHash<QString, QJsonObject>>;
140 enum RoomVisibility {
145 explicit Connection(QObject* parent =
nullptr);
146 explicit Connection(
const QUrl& server, QObject* parent =
nullptr);
147 ~Connection() override;
157 Q_INVOKABLE QVector<Quotient::Room*> allRooms()
const;
166 Q_INVOKABLE QVector<Quotient::Room*>
167 rooms(Quotient::JoinStates joinStates)
const;
170 Q_INVOKABLE
int roomsCount(Quotient::JoinStates joinStates)
const;
175 bool hasAccountData(
const QString& type)
const;
183 const EventPtr& accountData(
const QString& type)
const;
192 template <EventClass EventT>
193 const EventT* accountData()
const
196 return eventCast<EventT>(accountData(EventT::TypeId));
199 template <EventClass EventT>
200 const EventT* accountData(
const QString& keyName)
const
202 return eventCast<EventT>(accountData(keyName));
210 Q_INVOKABLE QJsonObject accountDataJson(
const QString& type)
const;
213 void setAccountData(EventPtr&& event);
215 Q_INVOKABLE
void setAccountData(
const QString& type,
216 const QJsonObject& content);
219 QStringList accountDataEventTypes()
const;
225 QHash<QString, QVector<Room*>> tagsToRooms()
const;
228 QStringList tagNames()
const;
231 QVector<Room*> roomsWithTag(
const QString& tagName)
const;
239 void addToDirectChats(
const Room* room,
const QString& userId);
250 void removeFromDirectChats(
const QString& roomId,
const QString& userId = {});
253 bool isDirectChat(
const QString& roomId)
const;
256 DirectChatsMap directChats()
const;
262 QList<QString> directChatMemberIds(
const Room* room)
const;
265 Q_INVOKABLE
bool isIgnored(
const QString& userId)
const;
268 [[deprecated(
"Use the overload accepting UserId instead")]]
269 Q_INVOKABLE
bool isIgnored(
const Quotient::User* user)
const;
272 Q_INVOKABLE Quotient::IgnoredUsersList ignoredUsers()
const;
279 Q_INVOKABLE
void addToIgnoredUsers(
const QString& userId);
285 Q_INVOKABLE
void removeFromIgnoredUsers(
const QString& userId);
290 Q_INVOKABLE QStringList userIds()
const;
293 QUrl homeserver()
const;
295 QString domain()
const;
297 [[deprecated(
"Check the result returned by Connection::loginFlows() instead")]]
298 bool isUsable()
const;
300 QVector<GetLoginFlowsJob::LoginFlow> loginFlows()
const;
302 bool supportsPasswordAuth()
const;
304 bool supportsSso()
const;
306 Q_INVOKABLE Quotient::Room* room(
307 const QString& roomId,
308 Quotient::JoinStates states = JoinState::Invite | JoinState::Join)
const;
310 Q_INVOKABLE Quotient::Room* roomByAlias(
311 const QString& roomAlias,
312 Quotient::JoinStates states = JoinState::Invite | JoinState::Join)
const;
318 void updateRoomAliases(
const QString& roomId,
319 const QStringList& previousRoomAliases,
320 const QStringList& roomAliases);
321 Q_INVOKABLE Quotient::Room* invitation(
const QString& roomId)
const;
322 Q_INVOKABLE Quotient::User* user(
const QString& uId);
323 const User* user()
const;
325 QString userId()
const;
328 Avatar& userAvatar(
const QUrl& avatarUrl);
331 Avatar& userAvatar(
const QString& avatarMediaId);
333 QString deviceId()
const;
334 QByteArray accessToken()
const;
335 bool isLoggedIn()
const;
340 bool isOnline()
const;
341 QOlmAccount* olmAccount()
const;
342 Database* database()
const;
344 std::unordered_map<QByteArray, QOlmInboundGroupSession> loadRoomMegolmSessions(
345 const Room* room)
const;
346 void saveMegolmSession(
const Room* room,
347 const QOlmInboundGroupSession& session,
const QByteArray &senderKey,
const QByteArray& senderEdKey)
const;
349 QString edKeyForUserDevice(
const QString& userId,
350 const QString& deviceId)
const;
351 QString curveKeyForUserDevice(
const QString& userId,
352 const QString& device)
const;
353 bool hasOlmSession(
const QString& user,
const QString& deviceId)
const;
356 void sendToDevice(
const QString& targetUserId,
const QString& targetDeviceId,
357 const Event& event,
bool encrypted);
360 bool isVerifiedSession(
const QByteArray& megolmSessionId)
const;
363 bool isVerifiedDevice(
const QString& userId,
const QString& deviceId)
const;
369 bool isKnownE2eeCapableDevice(
const QString& userId,
const QString& deviceId)
const;
372 void sendSessionKeyToDevices(
const QString& roomId,
373 const QOlmOutboundGroupSession& outboundSession,
374 const QMultiHash<QString, QString>& devices);
376 QJsonObject decryptNotification(
const QJsonObject ¬ification);
377 QStringList devicesForUser(
const QString& userId)
const;
378 Q_INVOKABLE
bool isQueryingKeys()
const;
380 QFuture<QByteArray> requestKeyFromDevices(event_type_t name);
382 QString masterKeyForUser(
const QString& userId)
const;
383 Q_INVOKABLE
bool isUserVerified(
const QString& userId)
const;
384 Q_INVOKABLE
bool allSessionsSelfVerified(
const QString& userId)
const;
385 bool hasConflictingDeviceIdsAndCrossSigningKeys(
const QString& userId);
387 void reloadDevices();
389 Q_INVOKABLE Quotient::SyncJob* syncJob()
const;
390 Q_INVOKABLE QString nextBatchToken()
const;
391 Q_INVOKABLE
int millisToReconnect()
const;
393 Q_INVOKABLE
void getTurnServers();
395 struct SupportedRoomVersion {
399 static constexpr QStringView StableTag = u"stable";
400 bool isStable()
const {
return status == StableTag; }
402 friend QDebug operator<<(QDebug dbg,
const SupportedRoomVersion& v)
404 QDebugStateSaver _(dbg);
405 return dbg.nospace() << v.id <<
'/' << v.status;
410 Q_INVOKABLE
bool capabilitiesReady()
const;
412 [[deprecated(
"Use capabilitiesReady() instead; don't forget to negate the returned value")]]
413 Q_INVOKABLE
bool loadingCapabilities()
const;
416 QStringList supportedMatrixSpecVersions()
const;
422 QString defaultRoomVersion()
const;
427 QStringList stableRoomVersions()
const;
431 QVector<SupportedRoomVersion> availableRoomVersions()
const;
436 bool canChangePassword()
const;
440 bool encryptionEnabled()
const;
447 void enableEncryption(
bool enable);
454 bool directChatEncryptionEnabled()
const;
461 void enableDirectChatEncryption(
bool enable);
467 Q_INVOKABLE
void loadState();
475 Q_INVOKABLE
void saveState()
const;
478 void saveRoomState(Room* r)
const;
482 Q_INVOKABLE QString stateCachePath()
const;
494 QDir stateCacheDir()
const;
498 bool cacheState()
const;
499 void setCacheState(
bool newValue);
501 bool lazyLoading()
const;
502 void setLazyLoading(
bool newValue);
505 Q_INVOKABLE BaseJob* run(BaseJob* job, RunningPolicy runningPolicy = ForegroundRequest);
512 template <std::derived_from<BaseJob> JobT>
513 requires (!std::same_as<JobT, BaseJob>)
514 JobHandle<JobT> run(JobT* job, RunningPolicy runningPolicy = ForegroundRequest)
516 JobHandle jh { job };
517 run(
static_cast<BaseJob*>(job), runningPolicy);
532 template <
typename JobT,
typename... JobArgTs>
533 JobHandle<JobT> callApi(RunningPolicy runningPolicy, JobArgTs&&... jobArgs)
535 return run(
new JobT(std::forward<JobArgTs>(jobArgs)...), runningPolicy);
541 template <
typename JobT,
typename... JobArgTs>
542 JobHandle<JobT> callApi(JobArgTs&&... jobArgs)
544 return callApi<JobT>(ForegroundRequest, std::forward<JobArgTs>(jobArgs)...);
551 template <
typename JobT,
typename... JobArgTs>
552 QUrl getUrlForApi(JobArgTs&&... jobArgs)
const
554 return JobT::makeRequestUrl(homeserverData(), std::forward<JobArgTs>(jobArgs)...);
568 Q_INVOKABLE SsoSession* prepareForSso(
const QString& initialDeviceName,
569 const QString& deviceId = {});
574 Q_INVOKABLE QString generateTxnId()
const;
577 Q_INVOKABLE QUrl makeMediaUrl(QUrl mxcUrl)
const;
579 Q_INVOKABLE
bool roomSucceeds(
const QString& maybePredecessorId,
580 const QString& maybeSuccessorId)
const;
583 static void setEncryptionDefault(
bool useByDefault);
586 static void setDirectChatEncryptionDefault(
bool useByDefault);
589 static void setRoomFactory(room_factory_t f);
592 static void setUserFactory(user_factory_t f);
595 static room_factory_t roomFactory();
598 static user_factory_t userFactory();
601 template <
typename T>
602 static void setRoomType()
604 setRoomFactory(defaultRoomFactory<T>);
608 template <
typename T>
609 static void setUserType()
611 setUserFactory(defaultUserFactory<T>);
626 Q_INVOKABLE
void resolveServer(
const QString& mxid);
631 Q_INVOKABLE QFuture<QList<LoginFlow> > setHomeserver(
const QUrl& baseUrl);
634 Q_INVOKABLE QFuture<Room*> getDirectChat(
const QString& otherUserId);
642 Q_INVOKABLE JobHandle<CreateRoomJob> createDirectChat(
const QString& userId,
643 const QString& topic = {},
644 const QString& name = {});
646 Q_INVOKABLE JobHandle<JoinRoomJob> joinRoom(
const QString& roomAlias,
647 const QStringList& serverNames = {});
649 Q_INVOKABLE QFuture<Room*> joinAndGetRoom(
const QString& roomAlias,
650 const QStringList& serverNames = {});
660 void loginWithPassword(
const QString& userId,
const QString& password,
661 const QString& initialDeviceName,
662 const QString& deviceId = {});
671 void loginWithToken(
const QString& loginToken,
672 const QString& initialDeviceName,
673 const QString& deviceId = {});
680 void assumeIdentity(
const QString& mxId,
const QString& deviceId,
const QString& accessToken);
685 JobHandle<GetVersionsJob> loadVersions();
688 JobHandle<GetCapabilitiesJob> loadCapabilities();
690 [[deprecated(
"Use loadCapabilities() instead")]]
void reloadCapabilities();
692 QFuture<
void> logout();
694 void sync(
int timeout = -1);
695 void syncLoop(
int timeout = 30000);
699 virtual MediaThumbnailJob*
700 getThumbnail(
const QString& mediaId, QSize requestedSize,
701 RunningPolicy policy = BackgroundRequest);
702 MediaThumbnailJob* getThumbnail(
const QUrl& url, QSize requestedSize,
703 RunningPolicy policy = BackgroundRequest);
704 MediaThumbnailJob* getThumbnail(
const QUrl& url,
int requestedWidth,
706 RunningPolicy policy = BackgroundRequest);
709 JobHandle<UploadContentJob> uploadContent(QIODevice* contentSource,
const QString& filename = {},
710 const QString& overrideContentType = {});
711 JobHandle<UploadContentJob> uploadFile(
const QString& fileName,
712 const QString& overrideContentType = {});
713 [[deprecated(
"Use downloadFile() instead")]] BaseJob* getContent(
const QString& mediaId);
714 [[deprecated(
"Use downloadFile() instead")]] BaseJob* getContent(
const QUrl& url);
717 DownloadFileJob* downloadFile(
const QUrl& url,
const QString& localFilename = {});
719 DownloadFileJob* downloadFile(
const QUrl& url,
720 const EncryptedFileMetadata& fileMetadata,
721 const QString& localFilename = {});
727 JobHandle<CreateRoomJob> createRoom(RoomVisibility visibility,
const QString& alias,
728 const QString& name,
const QString& topic, QStringList invites,
729 const QString& presetName = {},
const QString& roomVersion = {},
730 bool isDirect =
false,
731 const QVector<CreateRoomJob::StateEvent>& initialState = {},
732 const QVector<CreateRoomJob::Invite3pid>& invite3pids = {},
733 const QJsonObject& creationContent = {});
741 void requestDirectChat(
const QString& userId);
755 ForgetRoomJob* forgetRoom(
const QString& id);
757 SendToDeviceJob* sendToDevices(
const QString& eventType,
758 const UsersToDevicesToContent& contents);
760 [[deprecated(
"This method is experimental and may be removed any time")]]
761 SendMessageJob* sendMessage(
const QString& roomId,
const RoomEvent& event);
764 virtual LeaveRoomJob* leaveRoom(Room* room);
766 Quotient::KeyVerificationSession* startKeyVerificationSession(
const QString& userId,
767 const QString& deviceId);
769 Q_INVOKABLE
void startSelfVerification();
770 void encryptionUpdate(
const Room* room,
const QStringList& invitedIds = {});
772 static Connection* makeMockConnection(
const QString& mxId,
773 bool enableEncryption =
true);
781 void resolveError(QString error);
783 void homeserverChanged(QUrl baseUrl);
784 void loginFlowsChanged();
785 void capabilitiesLoaded();
798 void isOnlineChanged();
800 void loginError(QString message, QString details);
805 void requestFailed(Quotient::BaseJob* request);
817 void networkError(QString message, QString details,
int retriesTaken,
818 int nextRetryInMilliseconds);
821 void syncError(QString message, QString details);
823 void newUser(Quotient::User* user);
853 void newRoom(Quotient::Room* room);
860 void invitedRoom(Quotient::Room* room, Quotient::Room* prev);
870 void joinedRoom(Quotient::Room* room, Quotient::Room* prev);
880 void leftRoom(Quotient::Room* room, Quotient::Room* prev);
883 void aboutToDeleteRoom(Quotient::Room* room);
893 void createdRoom(Quotient::Room* room);
902 void loadedRoomState(Quotient::Room* room);
905 void accountDataChanged(QString type);
911 void directChatAvailable(Quotient::Room* directChat);
918 void directChatsListChanged(Quotient::DirectChatsMap additions,
919 Quotient::DirectChatsMap removals);
921 void ignoredUsersListChanged(Quotient::IgnoredUsersList additions,
922 Quotient::IgnoredUsersList removals);
924 void cacheStateChanged();
925 void lazyLoadingChanged();
926 void turnServersChanged(
const QJsonObject& servers);
927 void devicesListLoaded();
930 void encryptionChanged(
bool enabled);
931 void directChatsEncryptionChanged(
bool enabled);
933 void newKeyVerificationSession(Quotient::KeyVerificationSession* session);
934 void keyVerificationStateChanged(
935 const Quotient::KeyVerificationSession* session,
936 Quotient::KeyVerificationSession::State state);
937 void sessionVerified(
const QString& userId,
const QString& deviceId);
938 void finishedQueryingKeys();
939 void secretReceived(
const QString& requestId,
const QString& secret);
941 void userVerified(
const QString& userId);
945 void crossSigningSetupRequired();
951 friend class ::TestCrossSigning;
954 const ConnectionData* connectionData()
const;
957 HomeserverData homeserverData()
const;
977 Room* provideRoom(
const QString& id, std::optional<JoinState> joinState = {});
980 void onSyncSuccess(SyncData&& data,
bool fromCache =
false);
983 void syncLoopIteration();
989 static room_factory_t _roomFactory;
990 static user_factory_t _userFactory;
993 Q_DECLARE_METATYPE(Quotient::DirectChatsMap)
994 Q_DECLARE_METATYPE(Quotient::IgnoredUsersList)