aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/connection.cpp98
-rw-r--r--lib/connection.h13
2 files changed, 68 insertions, 43 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index aae4e94b..677a346b 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -133,10 +133,27 @@ public:
!= "json";
bool lazyLoading = false;
+ /** \brief Check the homeserver and resolve it if needed, before connecting
+ *
+ * A single entry for functions that need to check whether the homeserver
+ * is valid before running. May execute connectFn either synchronously
+ * or asynchronously. In case of errors, emits resolveError() if
+ * the homeserver URL is not valid and cannot be resolved from userId, or
+ * the homeserver doesn't support the requested login flow.
+ *
+ * \param userId fully-qualified MXID to resolve HS from
+ * \param connectFn a function to execute once the HS URL is good
+ * \param flow optionally, a login flow that should be supported for
+ * connectFn to work; `none`, if there's no login flow
+ * requirements
+ * \sa resolveServer, resolveError
+ */
+ void checkAndConnect(const QString &userId,
+ const std::function<void ()> &connectFn,
+ const std::optional<LoginFlows::LoginFlow> &flow = none);
template <typename... LoginArgTs>
void loginToServer(LoginArgTs&&... loginArgs);
- void assumeIdentity(const QString& userId, const QString& accessToken,
- const QString& deviceId);
+ void completeSetup(const QString& mxId);
void removeRoom(const QString& roomId);
void consumeRoomData(SyncDataList&& roomDataList, bool fromCache);
@@ -264,12 +281,15 @@ void Connection::resolveServer(const QString& mxid)
return;
}
- auto domain = maybeBaseUrl.host();
- qCDebug(MAIN) << "Finding the server" << domain;
+ qCDebug(MAIN) << "Finding the server" << maybeBaseUrl.host();
- d->data->setBaseUrl(maybeBaseUrl); // Just enough to check .well-known file
+ const auto& oldBaseUrl = d->data->baseUrl();
+ d->data->setBaseUrl(maybeBaseUrl); // Temporarily set it for this one call
d->resolverJob = callApi<GetWellknownJob>();
- connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl] {
+ connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl, oldBaseUrl] {
+ // Revert baseUrl so that setHomeserver() below triggers signals
+ // in case the base URL actually changed
+ d->data->setBaseUrl(oldBaseUrl);
if (d->resolverJob->error() != BaseJob::NotFoundError) {
if (!d->resolverJob->status().good()) {
qCWarning(MAIN)
@@ -297,6 +317,7 @@ void Connection::resolveServer(const QString& mxid)
<< "for base URL";
setHomeserver(maybeBaseUrl);
}
+ Q_ASSERT(d->loginFlowsJob != nullptr);
connect(d->loginFlowsJob, &BaseJob::success, this,
&Connection::resolved);
connect(d->loginFlowsJob, &BaseJob::failure, this, [this] {
@@ -324,10 +345,10 @@ void Connection::loginWithPassword(const QString& userId,
const QString& initialDeviceName,
const QString& deviceId)
{
- checkAndConnect(userId, [=] {
+ d->checkAndConnect(userId, [=] {
d->loginToServer(LoginFlows::Password.type, makeUserIdentifier(userId),
password, /*token*/ "", deviceId, initialDeviceName);
- });
+ }, LoginFlows::Password);
}
SsoSession* Connection::prepareForSso(const QString& initialDeviceName,
@@ -340,6 +361,7 @@ void Connection::loginWithToken(const QByteArray& loginToken,
const QString& initialDeviceName,
const QString& deviceId)
{
+ Q_ASSERT(d->data->baseUrl().isValid() && d->loginFlows.contains(LoginFlows::Token));
d->loginToServer(LoginFlows::Token.type,
none /*user is encoded in loginToken*/, "" /*password*/,
loginToken, deviceId, initialDeviceName);
@@ -348,8 +370,11 @@ void Connection::loginWithToken(const QByteArray& loginToken,
void Connection::assumeIdentity(const QString& mxId, const QString& accessToken,
const QString& deviceId)
{
- checkAndConnect(mxId,
- [=] { d->assumeIdentity(mxId, accessToken, deviceId); });
+ d->checkAndConnect(mxId, [this, mxId, accessToken, deviceId] {
+ d->data->setToken(accessToken.toLatin1());
+ d->data->setDeviceId(deviceId);
+ d->completeSetup(mxId);
+ });
}
void Connection::reloadCapabilities()
@@ -389,8 +414,9 @@ void Connection::Private::loginToServer(LoginArgTs&&... loginArgs)
auto loginJob =
q->callApi<LoginJob>(std::forward<LoginArgTs>(loginArgs)...);
connect(loginJob, &BaseJob::success, q, [this, loginJob] {
- assumeIdentity(loginJob->userId(), loginJob->accessToken(),
- loginJob->deviceId());
+ data->setToken(loginJob->accessToken().toLatin1());
+ data->setDeviceId(loginJob->deviceId());
+ completeSetup(loginJob->userId());
#ifndef Quotient_E2EE_ENABLED
qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off.";
#else // Quotient_E2EE_ENABLED
@@ -403,17 +429,14 @@ void Connection::Private::loginToServer(LoginArgTs&&... loginArgs)
});
}
-void Connection::Private::assumeIdentity(const QString& userId,
- const QString& accessToken,
- const QString& deviceId)
+void Connection::Private::completeSetup(const QString& mxId)
{
- data->setUserId(userId);
+ data->setUserId(mxId);
q->user(); // Creates a User object for the local user
- data->setToken(accessToken.toLatin1());
- data->setDeviceId(deviceId);
- q->setObjectName(userId % '/' % deviceId);
+ q->setObjectName(data->userId() % '/' % data->deviceId());
qCDebug(MAIN) << "Using server" << data->baseUrl().toDisplayString()
- << "by user" << userId << "from device" << deviceId;
+ << "by user" << data->userId()
+ << "from device" << data->deviceId();
#ifndef Quotient_E2EE_ENABLED
qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off.";
#else // Quotient_E2EE_ENABLED
@@ -430,22 +453,37 @@ void Connection::Private::assumeIdentity(const QString& userId,
q->reloadCapabilities();
}
-void Connection::checkAndConnect(const QString& userId,
- std::function<void()> connectFn)
+void Connection::Private::checkAndConnect(const QString& userId,
+ const std::function<void()>& connectFn,
+ const std::optional<LoginFlows::LoginFlow>& flow)
{
- if (d->data->baseUrl().isValid()) {
+ if (data->baseUrl().isValid() && (!flow || loginFlows.contains(*flow))) {
connectFn();
return;
}
- // Not good to go, try to fix the homeserver URL.
+ // Not good to go, try to ascertain the homeserver URL and flows
if (userId.startsWith('@') && userId.indexOf(':') != -1) {
- connectSingleShot(this, &Connection::homeserverChanged, this, connectFn);
- // NB: doResolveServer can emit resolveError, so this is a part of
- // checkAndConnect function contract.
- resolveServer(userId);
+ q->resolveServer(userId);
+ if (flow)
+ connectSingleShot(q, &Connection::loginFlowsChanged, q,
+ [this, flow, connectFn] {
+ if (loginFlows.contains(*flow))
+ connectFn();
+ else
+ emit q->loginError(
+ tr("The homeserver at %1 does not support"
+ " the login flow '%2'")
+ .arg(data->baseUrl().toDisplayString()),
+ flow->type);
+ });
+ else
+ connectSingleShot(q, &Connection::homeserverChanged, q, connectFn);
} else
- emit resolveError(tr("%1 is an invalid homeserver URL")
- .arg(d->data->baseUrl().toString()));
+ emit q->resolveError(tr("Please provide the fully-qualified user id"
+ " (such as @user:example.org) so that the"
+ " homeserver could be resolved; the current"
+ " homeserver URL(%1) is not good")
+ .arg(data->baseUrl().toDisplayString()));
}
void Connection::logout()
diff --git a/lib/connection.h b/lib/connection.h
index 36e20219..c90cb892 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -882,19 +882,6 @@ private:
class Private;
QScopedPointer<Private> d;
- /**
- * A single entry for functions that need to check whether the
- * homeserver is valid before running. May either execute connectFn
- * synchronously or asynchronously (if tryResolve is true and
- * a DNS lookup is initiated); in case of errors, emits resolveError
- * if the homeserver URL is not valid and cannot be resolved from
- * userId.
- *
- * @param userId - fully-qualified MXID to resolve HS from
- * @param connectFn - a function to execute once the HS URL is good
- */
- void checkAndConnect(const QString& userId, std::function<void()> connectFn);
-
static room_factory_t _roomFactory;
static user_factory_t _userFactory;
};