13 #include "csapi/create_room.h"
14 #include "csapi/login.h"
16 #include "e2ee/qolmoutboundsession.h"
18 #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>
26 #include <QtCore/QFuture>
30 Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow)
32 class TestCrossSigning;
44 class RoomMessagesJob;
47 class MediaThumbnailJob;
49 class UploadContentJob;
51 class DownloadFileJob;
52 class SendToDeviceJob;
56 struct EncryptedFileMetadata;
59 class QOlmInboundGroupSession;
61 using LoginFlow = GetLoginFlowsJob::LoginFlow;
64 namespace LoginFlows {
65 inline const LoginFlow Password {
"m.login.password"_ls };
66 inline const LoginFlow SSO {
"m.login.sso"_ls };
67 inline const LoginFlow Token {
"m.login.token"_ls };
72 inline bool operator==(
const LoginFlow& lhs,
const LoginFlow& rhs)
74 return lhs.type == rhs.type;
77 inline bool operator!=(
const LoginFlow& lhs,
const LoginFlow& rhs)
84 using room_factory_t =
85 std::function<Room*(Connection*,
const QString&, JoinState)>;
86 using user_factory_t = std::function<User*(Connection*,
const QString&)>;
92 template <
typename T = Room>
93 auto defaultRoomFactory(Connection* c,
const QString& id, JoinState js)
95 return new T(c, id, js);
102 template <
typename T = User>
103 auto defaultUserFactory(Connection* c,
const QString& id)
112 using DirectChatsMap = QMultiHash<
const User*, QString>;
113 using IgnoredUsersList = IgnoredUsersEvent::value_type;
118 Q_PROPERTY(User* localUser READ user NOTIFY stateChanged)
119 Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged)
120 Q_PROPERTY(QString domain READ domain NOTIFY stateChanged STORED
false)
121 Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged)
122 Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged)
123 Q_PROPERTY(
bool isLoggedIn READ isLoggedIn NOTIFY stateChanged STORED
false)
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);
288 QUrl homeserver()
const;
290 QString domain()
const;
292 bool isUsable()
const;
294 QVector<GetLoginFlowsJob::LoginFlow> loginFlows()
const;
296 bool supportsPasswordAuth()
const;
298 bool supportsSso()
const;
300 Q_INVOKABLE Quotient::Room* room(
301 const QString& roomId,
302 Quotient::JoinStates states = JoinState::Invite | JoinState::Join)
const;
304 Q_INVOKABLE Quotient::Room* roomByAlias(
305 const QString& roomAlias,
306 Quotient::JoinStates states = JoinState::Invite | JoinState::Join)
const;
312 void updateRoomAliases(
const QString& roomId,
313 const QStringList& previousRoomAliases,
314 const QStringList& roomAliases);
315 Q_INVOKABLE Quotient::Room* invitation(
const QString& roomId)
const;
316 Q_INVOKABLE Quotient::User* user(
const QString& uId);
317 const User* user()
const;
319 QString userId()
const;
322 Avatar& userAvatar(
const QUrl& avatarUrl);
325 Avatar& userAvatar(
const QString& avatarMediaId);
327 QString deviceId()
const;
328 QByteArray accessToken()
const;
329 bool isLoggedIn()
const;
330 QOlmAccount* olmAccount()
const;
331 Database* database()
const;
333 std::unordered_map<QByteArray, QOlmInboundGroupSession> loadRoomMegolmSessions(
334 const Room* room)
const;
335 void saveMegolmSession(
const Room* room,
336 const QOlmInboundGroupSession& session,
const QByteArray &senderKey)
const;
338 QString edKeyForUserDevice(
const QString& userId,
339 const QString& deviceId)
const;
340 QString curveKeyForUserDevice(
const QString& userId,
341 const QString& device)
const;
342 bool hasOlmSession(
const QString& user,
const QString& deviceId)
const;
345 void sendToDevice(
const QString& targetUserId,
const QString& targetDeviceId,
346 const Event& event,
bool encrypted);
349 bool isVerifiedSession(
const QByteArray& megolmSessionId)
const;
352 bool isVerifiedDevice(
const QString& userId,
const QString& deviceId)
const;
358 bool isKnownE2eeCapableDevice(
const QString& userId,
const QString& deviceId)
const;
361 void sendSessionKeyToDevices(
const QString& roomId,
362 const QOlmOutboundGroupSession& outboundSession,
363 const QMultiHash<QString, QString>& devices);
365 QJsonObject decryptNotification(
const QJsonObject ¬ification);
366 QStringList devicesForUser(
const QString& userId)
const;
367 Q_INVOKABLE
bool isQueryingKeys()
const;
369 QFuture<QByteArray> requestKeyFromDevices(event_type_t name);
371 QString masterKeyForUser(
const QString& userId)
const;
372 Q_INVOKABLE
bool isUserVerified(
const QString& userId)
const;
373 Q_INVOKABLE
bool allSessionsSelfVerified(
const QString& userId)
const;
374 bool hasConflictingDeviceIdsAndCrossSigningKeys(
const QString& userId);
376 void reloadDevices();
378 Q_INVOKABLE Quotient::SyncJob* syncJob()
const;
379 Q_INVOKABLE QString nextBatchToken()
const;
380 Q_INVOKABLE
int millisToReconnect()
const;
382 Q_INVOKABLE
void getTurnServers();
384 struct SupportedRoomVersion {
388 static const QString StableTag;
389 bool isStable()
const {
return status == StableTag; }
391 friend QDebug operator<<(QDebug dbg,
const SupportedRoomVersion& v)
393 QDebugStateSaver _(dbg);
394 return dbg.nospace() << v.id <<
'/' << v.status;
399 Q_INVOKABLE
bool loadingCapabilities()
const;
405 QString defaultRoomVersion()
const;
410 QStringList stableRoomVersions()
const;
414 QVector<SupportedRoomVersion> availableRoomVersions()
const;
419 bool canChangePassword()
const;
423 bool encryptionEnabled()
const;
430 void enableEncryption(
bool enable);
437 bool directChatEncryptionEnabled()
const;
444 void enableDirectChatEncryption(
bool enable);
450 Q_INVOKABLE
void loadState();
458 Q_INVOKABLE
void saveState()
const;
461 void saveRoomState(Room* r)
const;
465 Q_INVOKABLE QString stateCachePath()
const;
477 QDir stateCacheDir()
const;
481 bool cacheState()
const;
482 void setCacheState(
bool newValue);
484 bool lazyLoading()
const;
485 void setLazyLoading(
bool newValue);
488 Q_INVOKABLE BaseJob* run(BaseJob* job,
489 RunningPolicy runningPolicy = ForegroundRequest);
502 template <
typename JobT,
typename... JobArgTs>
503 JobHandle<JobT> callApi(RunningPolicy runningPolicy, JobArgTs&&... jobArgs)
505 auto job =
new JobT(std::forward<JobArgTs>(jobArgs)...);
506 run(job, runningPolicy);
513 template <
typename JobT,
typename... JobArgTs>
514 JobHandle<JobT> callApi(JobArgTs&&... jobArgs)
516 return callApi<JobT>(ForegroundRequest, std::forward<JobArgTs>(jobArgs)...);
523 template <
typename JobT,
typename... JobArgTs>
524 QUrl getUrlForApi(JobArgTs&&... jobArgs)
const
526 return JobT::makeRequestUrl(homeserver(),
527 std::forward<JobArgTs>(jobArgs)...);
541 Q_INVOKABLE SsoSession* prepareForSso(
const QString& initialDeviceName,
542 const QString& deviceId = {});
547 Q_INVOKABLE QString generateTxnId()
const;
550 Q_INVOKABLE QUrl makeMediaUrl(QUrl mxcUrl)
const;
552 Q_INVOKABLE
bool roomSucceeds(
const QString& maybePredecessorId,
553 const QString& maybeSuccessorId)
const;
556 static void setEncryptionDefault(
bool useByDefault);
559 static void setDirectChatEncryptionDefault(
bool useByDefault);
562 static void setRoomFactory(room_factory_t f);
565 static void setUserFactory(user_factory_t f);
568 static room_factory_t roomFactory();
571 static user_factory_t userFactory();
574 template <
typename T>
575 static void setRoomType()
577 setRoomFactory(defaultRoomFactory<T>);
581 template <
typename T>
582 static void setUserType()
584 setUserFactory(defaultUserFactory<T>);
599 Q_INVOKABLE
void resolveServer(
const QString& mxid);
604 Q_INVOKABLE QFuture<QList<LoginFlow> > setHomeserver(
const QUrl& baseUrl);
607 Q_INVOKABLE QFuture<Room*> getDirectChat(
const QString& otherUserId);
615 Q_INVOKABLE JobHandle<CreateRoomJob> createDirectChat(
const QString& userId,
616 const QString& topic = {},
617 const QString& name = {});
619 Q_INVOKABLE JobHandle<JoinRoomJob> joinRoom(
const QString& roomAlias,
620 const QStringList& serverNames = {});
622 Q_INVOKABLE QFuture<Room*> joinAndGetRoom(
const QString& roomAlias,
623 const QStringList& serverNames = {});
633 void loginWithPassword(
const QString& userId,
const QString& password,
634 const QString& initialDeviceName,
635 const QString& deviceId = {});
644 void loginWithToken(
const QString& loginToken,
645 const QString& initialDeviceName,
646 const QString& deviceId = {});
653 void assumeIdentity(
const QString& mxId,
const QString& accessToken);
656 void reloadCapabilities();
660 void sync(
int timeout = -1);
661 void syncLoop(
int timeout = 30000);
665 virtual MediaThumbnailJob*
666 getThumbnail(
const QString& mediaId, QSize requestedSize,
667 RunningPolicy policy = BackgroundRequest);
668 MediaThumbnailJob* getThumbnail(
const QUrl& url, QSize requestedSize,
669 RunningPolicy policy = BackgroundRequest);
670 MediaThumbnailJob* getThumbnail(
const QUrl& url,
int requestedWidth,
672 RunningPolicy policy = BackgroundRequest);
675 JobHandle<UploadContentJob> uploadContent(QIODevice* contentSource,
const QString& filename = {},
676 const QString& overrideContentType = {});
677 JobHandle<UploadContentJob> uploadFile(
const QString& fileName,
678 const QString& overrideContentType = {});
679 GetContentJob* getContent(
const QString& mediaId);
680 GetContentJob* getContent(
const QUrl& url);
682 DownloadFileJob* downloadFile(
const QUrl& url,
const QString& localFilename = {});
684 DownloadFileJob* downloadFile(
const QUrl& url,
685 const EncryptedFileMetadata& fileMetadata,
686 const QString& localFilename = {});
692 JobHandle<CreateRoomJob> createRoom(RoomVisibility visibility,
const QString& alias,
693 const QString& name,
const QString& topic, QStringList invites,
694 const QString& presetName = {},
const QString& roomVersion = {},
695 bool isDirect =
false,
696 const QVector<CreateRoomJob::StateEvent>& initialState = {},
697 const QVector<CreateRoomJob::Invite3pid>& invite3pids = {},
698 const QJsonObject& creationContent = {});
706 void requestDirectChat(
const QString& userId);
720 ForgetRoomJob* forgetRoom(
const QString& id);
722 SendToDeviceJob* sendToDevices(
const QString& eventType,
723 const UsersToDevicesToContent& contents);
725 [[deprecated(
"This method is experimental and may be removed any time")]]
726 SendMessageJob* sendMessage(
const QString& roomId,
const RoomEvent& event);
729 virtual LeaveRoomJob* leaveRoom(Room* room);
731 Quotient::KeyVerificationSession* startKeyVerificationSession(
const QString& userId,
732 const QString& deviceId);
734 Q_INVOKABLE
void startSelfVerification();
735 void encryptionUpdate(
const Room* room,
const QStringList& invitedIds = {});
737 static Connection* makeMockConnection(
const QString& mxId,
738 bool enableEncryption =
true);
746 void resolveError(QString error);
748 void homeserverChanged(QUrl baseUrl);
749 void loginFlowsChanged();
750 void capabilitiesLoaded();
761 void loginError(QString message, QString details);
766 void requestFailed(Quotient::BaseJob* request);
778 void networkError(QString message, QString details,
int retriesTaken,
779 int nextRetryInMilliseconds);
782 void syncError(QString message, QString details);
784 void newUser(Quotient::User* user);
814 void newRoom(Quotient::Room* room);
821 void invitedRoom(Quotient::Room* room, Quotient::Room* prev);
831 void joinedRoom(Quotient::Room* room, Quotient::Room* prev);
841 void leftRoom(Quotient::Room* room, Quotient::Room* prev);
844 void aboutToDeleteRoom(Quotient::Room* room);
854 void createdRoom(Quotient::Room* room);
863 void loadedRoomState(Quotient::Room* room);
866 void accountDataChanged(QString type);
872 void directChatAvailable(Quotient::Room* directChat);
879 void directChatsListChanged(Quotient::DirectChatsMap additions,
880 Quotient::DirectChatsMap removals);
882 void ignoredUsersListChanged(Quotient::IgnoredUsersList additions,
883 Quotient::IgnoredUsersList removals);
885 void cacheStateChanged();
886 void lazyLoadingChanged();
887 void turnServersChanged(
const QJsonObject& servers);
888 void devicesListLoaded();
891 void encryptionChanged(
bool enabled);
892 void directChatsEncryptionChanged(
bool enabled);
894 void newKeyVerificationSession(Quotient::KeyVerificationSession* session);
895 void keyVerificationStateChanged(
896 const Quotient::KeyVerificationSession* session,
897 Quotient::KeyVerificationSession::State state);
898 void sessionVerified(
const QString& userId,
const QString& deviceId);
899 void finishedQueryingKeys();
900 void secretReceived(
const QString& requestId,
const QString& secret);
902 void userVerified(
const QString& userId);
904 friend class ::TestCrossSigning;
907 const ConnectionData* connectionData()
const;
927 Room* provideRoom(
const QString& id, std::optional<JoinState> joinState = {});
930 void onSyncSuccess(SyncData&& data,
bool fromCache =
false);
933 void syncLoopIteration();
939 static room_factory_t _roomFactory;
940 static user_factory_t _userFactory;
943 Q_DECLARE_METATYPE(Quotient::DirectChatsMap)
944 Q_DECLARE_METATYPE(Quotient::IgnoredUsersList)