aboutsummaryrefslogtreecommitdiff
path: root/lib/connection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/connection.cpp')
-rw-r--r--lib/connection.cpp1016
1 files changed, 469 insertions, 547 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index b9ab5147..6cd6ad0b 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -13,42 +13,45 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "connection.h"
+
#include "connectiondata.h"
-#include "user.h"
-#include "events/directchatevent.h"
-#include "events/eventloader.h"
+#include "encryptionmanager.h"
#include "room.h"
#include "settings.h"
-#include "encryptionmanager.h"
-#include "csapi/login.h"
+#include "user.h"
+
+#include "csapi/account-data.h"
#include "csapi/capabilities.h"
+#include "csapi/joining.h"
+#include "csapi/leaving.h"
+#include "csapi/login.h"
#include "csapi/logout.h"
#include "csapi/receipts.h"
-#include "csapi/leaving.h"
-#include "csapi/account-data.h"
-#include "csapi/joining.h"
-#include "csapi/to_device.h"
#include "csapi/room_send.h"
-#include "csapi/wellknown.h"
+#include "csapi/to_device.h"
#include "csapi/versions.h"
-#include "jobs/syncjob.h"
-#include "jobs/mediathumbnailjob.h"
-#include "jobs/downloadfilejob.h"
#include "csapi/voip.h"
+#include "csapi/wellknown.h"
-#include <QtNetwork/QDnsLookup>
-#include <QtCore/QFile>
+#include "events/directchatevent.h"
+#include "events/eventloader.h"
+#include "jobs/downloadfilejob.h"
+#include "jobs/mediathumbnailjob.h"
+#include "jobs/syncjob.h"
+
+#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
-#include <QtCore/QStandardPaths>
-#include <QtCore/QStringBuilder>
#include <QtCore/QElapsedTimer>
-#include <QtCore/QRegularExpression>
+#include <QtCore/QFile>
#include <QtCore/QMimeDatabase>
-#include <QtCore/QCoreApplication>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QStringBuilder>
+#include <QtNetwork/QDnsLookup>
using namespace QMatrixClient;
@@ -57,10 +60,8 @@ template <typename HashT, typename Pred>
HashT erase_if(HashT& hashMap, Pred pred)
{
HashT removals;
- for (auto it = hashMap.begin(); it != hashMap.end();)
- {
- if (pred(it))
- {
+ for (auto it = hashMap.begin(); it != hashMap.end();) {
+ if (pred(it)) {
removals.insert(it.key(), it.value());
it = hashMap.erase(it);
} else
@@ -69,94 +70,92 @@ HashT erase_if(HashT& hashMap, Pred pred)
return removals;
}
-class Connection::Private
-{
- public:
- explicit Private(std::unique_ptr<ConnectionData>&& connection)
- : data(move(connection))
- { }
- Q_DISABLE_COPY(Private)
- DISABLE_MOVE(Private)
-
- Connection* q = nullptr;
- std::unique_ptr<ConnectionData> data;
- // A complex key below is a pair of room name and whether its
- // state is Invited. The spec mandates to keep Invited room state
- // separately; specifically, we should keep objects for Invite and
- // Leave state of the same room if the two happen to co-exist.
- QHash<QPair<QString, bool>, Room*> roomMap;
- /// Mapping from serverparts to alias/room id mappings,
- /// as of the last sync
- QHash<QString, QHash<QString, QString>> roomAliasMap;
- QVector<QString> roomIdsToForget;
- QVector<Room*> firstTimeRooms;
- QVector<QString> pendingStateRoomIds;
- QMap<QString, User*> userMap;
- DirectChatsMap directChats;
- DirectChatUsersMap directChatUsers;
- // The below two variables track local changes between sync completions.
- // See also: https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events
- DirectChatsMap dcLocalAdditions;
- DirectChatsMap dcLocalRemovals;
- std::unordered_map<QString, EventPtr> accountData;
- QString userId;
- int syncLoopTimeout = -1;
-
- GetCapabilitiesJob* capabilitiesJob = nullptr;
- GetCapabilitiesJob::Capabilities capabilities;
-
- QScopedPointer<EncryptionManager> encryptionManager;
-
- SyncJob* syncJob = nullptr;
-
- bool cacheState = true;
- bool cacheToBinary = SettingsGroup("libqmatrixclient")
- .value("cache_type").toString() != "json";
- bool lazyLoading = false;
-
- void connectWithToken(const QString& user, const QString& accessToken,
- const QString& deviceId);
- void removeRoom(const QString& roomId);
-
- template <typename EventT>
- EventT* unpackAccountData() const
- {
- const auto& eventIt = accountData.find(EventT::matrixTypeId());
- return eventIt == accountData.end()
- ? nullptr : weakPtrCast<EventT>(eventIt->second);
- }
+class Connection::Private {
+public:
+ explicit Private(std::unique_ptr<ConnectionData>&& connection)
+ : data(move(connection))
+ {}
+ Q_DISABLE_COPY(Private)
+ DISABLE_MOVE(Private)
+
+ Connection* q = nullptr;
+ std::unique_ptr<ConnectionData> data;
+ // A complex key below is a pair of room name and whether its
+ // state is Invited. The spec mandates to keep Invited room state
+ // separately; specifically, we should keep objects for Invite and
+ // Leave state of the same room if the two happen to co-exist.
+ QHash<QPair<QString, bool>, Room*> roomMap;
+ /// Mapping from serverparts to alias/room id mappings,
+ /// as of the last sync
+ QHash<QString, QHash<QString, QString>> roomAliasMap;
+ QVector<QString> roomIdsToForget;
+ QVector<Room*> firstTimeRooms;
+ QVector<QString> pendingStateRoomIds;
+ QMap<QString, User*> userMap;
+ DirectChatsMap directChats;
+ DirectChatUsersMap directChatUsers;
+ // The below two variables track local changes between sync completions.
+ // See also:
+ // https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events
+ DirectChatsMap dcLocalAdditions;
+ DirectChatsMap dcLocalRemovals;
+ std::unordered_map<QString, EventPtr> accountData;
+ QString userId;
+ int syncLoopTimeout = -1;
+
+ GetCapabilitiesJob* capabilitiesJob = nullptr;
+ GetCapabilitiesJob::Capabilities capabilities;
+
+ QScopedPointer<EncryptionManager> encryptionManager;
+
+ SyncJob* syncJob = nullptr;
+
+ bool cacheState = true;
+ bool cacheToBinary =
+ SettingsGroup("libqmatrixclient").value("cache_type").toString()
+ != "json";
+ bool lazyLoading = false;
+
+ void connectWithToken(const QString& user, const QString& accessToken,
+ const QString& deviceId);
+ void removeRoom(const QString& roomId);
+
+ template <typename EventT>
+ EventT* unpackAccountData() const
+ {
+ const auto& eventIt = accountData.find(EventT::matrixTypeId());
+ return eventIt == accountData.end()
+ ? nullptr
+ : weakPtrCast<EventT>(eventIt->second);
+ }
- void packAndSendAccountData(EventPtr&& event)
- {
- const auto eventType = event->matrixType();
- q->callApi<SetAccountDataJob>(userId, eventType,
- event->contentJson());
- accountData[eventType] = std::move(event);
- emit q->accountDataChanged(eventType);
- }
+ void packAndSendAccountData(EventPtr&& event)
+ {
+ const auto eventType = event->matrixType();
+ q->callApi<SetAccountDataJob>(userId, eventType, event->contentJson());
+ accountData[eventType] = std::move(event);
+ emit q->accountDataChanged(eventType);
+ }
- template <typename EventT, typename ContentT>
- void packAndSendAccountData(ContentT&& content)
- {
- packAndSendAccountData(
- makeEvent<EventT>(std::forward<ContentT>(content)));
- }
- QString topLevelStatePath() const
- {
- return q->stateCacheDir().filePath("state.json");
- }
+ template <typename EventT, typename ContentT>
+ void packAndSendAccountData(ContentT&& content)
+ {
+ packAndSendAccountData(
+ makeEvent<EventT>(std::forward<ContentT>(content)));
+ }
+ QString topLevelStatePath() const
+ {
+ return q->stateCacheDir().filePath("state.json");
+ }
};
Connection::Connection(const QUrl& server, QObject* parent)
- : QObject(parent)
- , d(new Private(std::make_unique<ConnectionData>(server)))
+ : QObject(parent), d(new Private(std::make_unique<ConnectionData>(server)))
{
d->q = this; // All d initialization should occur before this line
}
-Connection::Connection(QObject* parent)
- : Connection({}, parent)
-{ }
+Connection::Connection(QObject* parent) : Connection({}, parent) {}
Connection::~Connection()
{
@@ -168,11 +167,9 @@ void Connection::resolveServer(const QString& mxid)
{
auto maybeBaseUrl = QUrl::fromUserInput(serverPart(mxid));
maybeBaseUrl.setScheme("https"); // Instead of the Qt-default "http"
- if (maybeBaseUrl.isEmpty() || !maybeBaseUrl.isValid())
- {
- emit resolveError(
- tr("%1 is not a valid homeserver address")
- .arg(maybeBaseUrl.toString()));
+ if (maybeBaseUrl.isEmpty() || !maybeBaseUrl.isValid()) {
+ emit resolveError(tr("%1 is not a valid homeserver address")
+ .arg(maybeBaseUrl.toString()));
return;
}
@@ -182,91 +179,94 @@ void Connection::resolveServer(const QString& mxid)
qCDebug(MAIN) << "Finding the server" << domain;
auto getWellKnownJob = callApi<GetWellknownJob>();
- connect(getWellKnownJob, &BaseJob::finished, [this, getWellKnownJob, maybeBaseUrl] {
- if (getWellKnownJob->status() == BaseJob::NotFoundError) {
- qCDebug(MAIN) << "No .well-known file, IGNORE";
- } else if (getWellKnownJob->status() != BaseJob::Success) {
- qCDebug(MAIN) << "Fetching .well-known file failed, FAIL_PROMPT";
- emit resolveError(tr("Fetching .well-known file failed"));
- return;
- } else if (getWellKnownJob->data().homeserver.baseUrl.isEmpty()) {
- qCDebug(MAIN) << "base_url not provided, FAIL_PROMPT";
- emit resolveError(tr("base_url not provided"));
- return;
- } else if (!QUrl(getWellKnownJob->data().homeserver.baseUrl).isValid()) {
- qCDebug(MAIN) << "base_url invalid, FAIL_ERROR";
- emit resolveError(tr("base_url invalid"));
- return;
- } else {
- QUrl baseUrl(getWellKnownJob->data().homeserver.baseUrl);
-
- qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() << "is" << baseUrl.toString();
- setHomeserver(baseUrl);
- }
+ connect(
+ getWellKnownJob, &BaseJob::finished,
+ [this, getWellKnownJob, maybeBaseUrl] {
+ if (getWellKnownJob->status() == BaseJob::NotFoundError)
+ qCDebug(MAIN) << "No .well-known file, IGNORE";
+ else {
+ if (getWellKnownJob->status() != BaseJob::Success) {
+ qCDebug(MAIN)
+ << "Fetching .well-known file failed, FAIL_PROMPT";
+ emit resolveError(tr("Fetching .well-known file failed"));
+ return;
+ }
+ QUrl baseUrl(getWellKnownJob->data().homeserver.baseUrl);
+ if (baseUrl.isEmpty()) {
+ qCDebug(MAIN) << "base_url not provided, FAIL_PROMPT";
+ emit resolveError(tr("base_url not provided"));
+ return;
+ }
+ if (!baseUrl.isValid()) {
+ qCDebug(MAIN) << "base_url invalid, FAIL_ERROR";
+ emit resolveError(tr("base_url invalid"));
+ return;
+ }
+
+ qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host()
+ << "is" << baseUrl.toString();
+ setHomeserver(baseUrl);
+ }
- auto getVersionsJob = callApi<GetVersionsJob>();
+ auto getVersionsJob = callApi<GetVersionsJob>();
- connect(getVersionsJob, &BaseJob::finished, [this, getVersionsJob] {
- if (getVersionsJob->status() == BaseJob::Success) {
- qCDebug(MAIN) << "homeserver url is valid";
- emit resolved();
- } else {
- qCDebug(MAIN) << "homeserver url invalid";
- emit resolveError(tr("homeserver url invalid"));
- }
+ connect(getVersionsJob, &BaseJob::finished, [this, getVersionsJob] {
+ if (getVersionsJob->status() == BaseJob::Success) {
+ qCDebug(MAIN) << "homeserver url is valid";
+ emit resolved();
+ } else {
+ qCDebug(MAIN) << "homeserver url invalid";
+ emit resolveError(tr("homeserver url invalid"));
+ }
+ });
});
- });
}
void Connection::connectToServer(const QString& user, const QString& password,
const QString& initialDeviceName,
const QString& deviceId)
{
- checkAndConnect(user,
- [=] {
- doConnectToServer(user, password, initialDeviceName, deviceId);
- });
+ checkAndConnect(user, [=] {
+ doConnectToServer(user, password, initialDeviceName, deviceId);
+ });
}
void Connection::doConnectToServer(const QString& user, const QString& password,
const QString& initialDeviceName,
const QString& deviceId)
{
- auto loginJob = callApi<LoginJob>(QStringLiteral("m.login.password"),
- UserIdentifier { QStringLiteral("m.id.user"),
- {{ QStringLiteral("user"), user }} },
- password, /*token*/ "", deviceId, initialDeviceName);
- connect(loginJob, &BaseJob::success, this,
- [this, loginJob] {
- d->connectWithToken(loginJob->userId(), loginJob->accessToken(),
- loginJob->deviceId());
-
- AccountSettings accountSettings(loginJob->userId());
- d->encryptionManager.reset(new EncryptionManager(accountSettings.encryptionAccountPickle()));
- if (accountSettings.encryptionAccountPickle().isEmpty()) {
- accountSettings.setEncryptionAccountPickle(d->encryptionManager->olmAccountPickle());
- }
-
- d->encryptionManager->uploadIdentityKeys(this);
- d->encryptionManager->uploadOneTimeKeys(this);
+ auto loginJob = callApi<LoginJob>(
+ QStringLiteral("m.login.password"),
+ UserIdentifier { QStringLiteral("m.id.user"),
+ { { QStringLiteral("user"), user } } },
+ password, /*token*/ "", deviceId, initialDeviceName);
+ connect(loginJob, &BaseJob::success, this, [this, loginJob] {
+ d->connectWithToken(loginJob->userId(), loginJob->accessToken(),
+ loginJob->deviceId());
+
+ AccountSettings accountSettings(loginJob->userId());
+ d->encryptionManager.reset(
+ new EncryptionManager(accountSettings.encryptionAccountPickle()));
+ if (accountSettings.encryptionAccountPickle().isEmpty()) {
+ accountSettings.setEncryptionAccountPickle(
+ d->encryptionManager->olmAccountPickle());
+ }
- });
- connect(loginJob, &BaseJob::failure, this,
- [this, loginJob] {
- emit loginError(loginJob->errorString(), loginJob->rawDataSample());
- });
+ d->encryptionManager->uploadIdentityKeys(this);
+ d->encryptionManager->uploadOneTimeKeys(this);
+ });
+ connect(loginJob, &BaseJob::failure, this, [this, loginJob] {
+ emit loginError(loginJob->errorString(), loginJob->rawDataSample());
+ });
}
-void Connection::syncLoopIteration()
-{
- sync(d->syncLoopTimeout);
-}
+void Connection::syncLoopIteration() { sync(d->syncLoopTimeout); }
void Connection::connectWithToken(const QString& userId,
const QString& accessToken,
const QString& deviceId)
{
checkAndConnect(userId,
- [=] { d->connectWithToken(userId, accessToken, deviceId); });
+ [=] { d->connectWithToken(userId, accessToken, deviceId); });
}
void Connection::reloadCapabilities()
@@ -278,18 +278,16 @@ void Connection::reloadCapabilities()
else if (d->capabilitiesJob->error() == BaseJob::IncorrectRequestError)
qCDebug(MAIN) << "Server doesn't support /capabilities";
- if (d->capabilities.roomVersions.omitted())
- {
+ if (d->capabilities.roomVersions.omitted()) {
qCWarning(MAIN) << "Pinning supported room version to 1";
- d->capabilities.roomVersions = { "1", {{ "1", "stable" }} };
+ d->capabilities.roomVersions = { "1", { { "1", "stable" } } };
} else {
- qCDebug(MAIN) << "Room versions:"
- << defaultRoomVersion() << "is default, full list:"
- << availableRoomVersions();
+ qCDebug(MAIN) << "Room versions:" << defaultRoomVersion()
+ << "is default, full list:" << availableRoomVersions();
}
Q_ASSERT(!d->capabilities.roomVersions.omitted());
emit capabilitiesLoaded();
- for (auto* r: d->roomMap)
+ for (auto* r : d->roomMap)
r->checkVersion();
});
}
@@ -320,30 +318,26 @@ void Connection::Private::connectWithToken(const QString& user,
void Connection::checkAndConnect(const QString& userId,
std::function<void()> connectFn)
{
- if (d->data->baseUrl().isValid())
- {
+ if (d->data->baseUrl().isValid()) {
connectFn();
return;
}
// Not good to go, try to fix the homeserver URL.
- if (userId.startsWith('@') && userId.indexOf(':') != -1)
- {
+ 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);
} else
- emit resolveError(
- tr("%1 is an invalid homeserver URL")
- .arg(d->data->baseUrl().toString()));
+ emit resolveError(tr("%1 is an invalid homeserver URL")
+ .arg(d->data->baseUrl().toString()));
}
void Connection::logout()
{
auto job = callApi<LogoutJob>();
- connect( job, &LogoutJob::finished, this, [job,this] {
- if (job->status().good() || job->error() == BaseJob::ContentAccessError)
- {
+ connect(job, &LogoutJob::finished, this, [job, this] {
+ if (job->status().good() || job->error() == BaseJob::ContentAccessError) {
stopSync();
d->data->setToken({});
emit stateChanged();
@@ -361,27 +355,25 @@ void Connection::sync(int timeout)
filter.room->timeline->limit = 100;
filter.room->state->lazyLoadMembers = d->lazyLoading;
auto job = d->syncJob = callApi<SyncJob>(BackgroundRequest,
- d->data->lastEvent(), filter, timeout);
- connect( job, &SyncJob::success, this, [this, job] {
+ d->data->lastEvent(), filter,
+ timeout);
+ connect(job, &SyncJob::success, this, [this, job] {
onSyncSuccess(job->takeData());
d->syncJob = nullptr;
emit syncDone();
});
- connect( job, &SyncJob::retryScheduled, this,
- [this,job] (int retriesTaken, int nextInMilliseconds)
- {
- emit networkError(job->errorString(), job->rawDataSample(),
- retriesTaken, nextInMilliseconds);
- });
- connect( job, &SyncJob::failure, this, [this, job] {
+ connect(job, &SyncJob::retryScheduled, this,
+ [this, job](int retriesTaken, int nextInMilliseconds) {
+ emit networkError(job->errorString(), job->rawDataSample(),
+ retriesTaken, nextInMilliseconds);
+ });
+ connect(job, &SyncJob::failure, this, [this, job] {
d->syncJob = nullptr;
- if (job->error() == BaseJob::ContentAccessError)
- {
- qCWarning(SYNCJOB)
- << "Sync job failed with ContentAccessError - login expired?";
+ if (job->error() == BaseJob::ContentAccessError) {
+ qCWarning(SYNCJOB) << "Sync job failed with ContentAccessError - "
+ "login expired?";
emit loginError(job->errorString(), job->rawDataSample());
- }
- else
+ } else
emit syncError(job->errorString(), job->rawDataSample());
});
}
@@ -396,8 +388,7 @@ void Connection::syncLoop(int timeout)
QJsonObject toJson(const Connection::DirectChatsMap& directChats)
{
QJsonObject json;
- for (auto it = directChats.begin(); it != directChats.end();)
- {
+ for (auto it = directChats.begin(); it != directChats.end();) {
QJsonArray roomIds;
const auto* user = it.key();
for (; it != directChats.end() && it.key() == user; ++it)
@@ -407,31 +398,28 @@ QJsonObject toJson(const Connection::DirectChatsMap& directChats)
return json;
}
-void Connection::onSyncSuccess(SyncData &&data, bool fromCache) {
+void Connection::onSyncSuccess(SyncData&& data, bool fromCache)
+{
d->data->setLastEvent(data.nextBatch());
- for (auto&& roomData: data.takeRoomData())
- {
+ for (auto&& roomData : data.takeRoomData()) {
const auto forgetIdx = d->roomIdsToForget.indexOf(roomData.roomId);
- if (forgetIdx != -1)
- {
+ if (forgetIdx != -1) {
d->roomIdsToForget.removeAt(forgetIdx);
- if (roomData.joinState == JoinState::Leave)
- {
- qDebug(MAIN) << "Room" << roomData.roomId
+ if (roomData.joinState == JoinState::Leave) {
+ qDebug(MAIN)
+ << "Room" << roomData.roomId
<< "has been forgotten, ignoring /sync response for it";
continue;
}
qWarning(MAIN) << "Room" << roomData.roomId
- << "has just been forgotten but /sync returned it in"
- << toCString(roomData.joinState)
- << "state - suspiciously fast turnaround";
+ << "has just been forgotten but /sync returned it in"
+ << toCString(roomData.joinState)
+ << "state - suspiciously fast turnaround";
}
- if ( auto* r = provideRoom(roomData.roomId, roomData.joinState) )
- {
+ if (auto* r = provideRoom(roomData.roomId, roomData.joinState)) {
d->pendingStateRoomIds.removeOne(roomData.roomId);
r->updateData(std::move(roomData), fromCache);
- if (d->firstTimeRooms.removeOne(r))
- {
+ if (d->firstTimeRooms.removeOne(r)) {
emit loadedRoomState(r);
if (!d->capabilities.roomVersions.omitted())
r->checkVersion();
@@ -443,79 +431,74 @@ void Connection::onSyncSuccess(SyncData &&data, bool fromCache) {
}
// After running this loop, the account data events not saved in
// d->accountData (see the end of the loop body) are auto-cleaned away
- for (auto& eventPtr : data.takeAccountData())
- {
- visit(*eventPtr,
- [this](const DirectChatEvent& dce) {
- // See https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events
- const auto& usersToDCs = dce.usersToDirectChats();
- DirectChatsMap remoteRemovals =
- erase_if(d->directChats, [&usersToDCs, this](auto it) {
- return !(usersToDCs.contains(it.key()->id(), it.value())
- || d->dcLocalAdditions.contains(it.key(),
- it.value()));
- });
- erase_if(d->directChatUsers, [&remoteRemovals](auto it) {
- return remoteRemovals.contains(it.value(), it.key());
- });
- // Remove from dcLocalRemovals what the server already has.
- erase_if(d->dcLocalRemovals, [&remoteRemovals](auto it) {
- return remoteRemovals.contains(it.key(), it.value());
- });
- if (MAIN().isDebugEnabled())
- for (auto it = remoteRemovals.begin();
- it != remoteRemovals.end(); ++it) {
- qCDebug(MAIN)
- << it.value() << "is no more a direct chat with"
- << it.key()->id();
- }
-
- DirectChatsMap remoteAdditions;
- for (auto it = usersToDCs.begin(); it != usersToDCs.end();
- ++it) {
- if (auto* u = user(it.key())) {
- if (!d->directChats.contains(u, it.value())
- && !d->dcLocalRemovals.contains(u, it.value()))
- {
- Q_ASSERT(
- !d->directChatUsers.contains(it.value(), u));
- remoteAdditions.insert(u, it.value());
- d->directChats.insert(u, it.value());
- d->directChatUsers.insert(it.value(), u);
- qCDebug(MAIN)
- << "Marked room" << it.value()
- << "as a direct chat with" << u->id();
- }
- } else
- qCWarning(MAIN) << "Couldn't get a user object for"
- << it.key();
- }
- // Remove from dcLocalAdditions what the server already has.
- erase_if(d->dcLocalAdditions, [&remoteAdditions](auto it) {
- return remoteAdditions.contains(it.key(), it.value());
- });
- if (!remoteAdditions.isEmpty() || !remoteRemovals.isEmpty())
- emit directChatsListChanged(remoteAdditions, remoteRemovals);
- },
- // catch-all, passing eventPtr for a possible take-over
- [this, &eventPtr](const Event& accountEvent) {
- if (is<IgnoredUsersEvent>(accountEvent))
- qCDebug(MAIN)
- << "Users ignored by" << d->userId << "updated:"
- << QStringList::fromSet(ignoredUsers()).join(',');
-
- auto& currentData = d->accountData[accountEvent.matrixType()];
- // A polymorphic event-specific comparison might be a bit more
- // efficient; maaybe do it another day
- if (!currentData
- || currentData->contentJson()
- != accountEvent.contentJson()) {
- currentData = std::move(eventPtr);
- qCDebug(MAIN) << "Updated account data of type"
- << currentData->matrixType();
- emit accountDataChanged(currentData->matrixType());
- }
- });
+ for (auto& eventPtr : data.takeAccountData()) {
+ visit(
+ *eventPtr,
+ [this](const DirectChatEvent& dce) {
+ // https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events
+ const auto& usersToDCs = dce.usersToDirectChats();
+ DirectChatsMap remoteRemovals =
+ erase_if(d->directChats, [&usersToDCs, this](auto it) {
+ return !(usersToDCs.contains(it.key()->id(), it.value())
+ || d->dcLocalAdditions.contains(it.key(),
+ it.value()));
+ });
+ erase_if(d->directChatUsers, [&remoteRemovals](auto it) {
+ return remoteRemovals.contains(it.value(), it.key());
+ });
+ // Remove from dcLocalRemovals what the server already has.
+ erase_if(d->dcLocalRemovals, [&remoteRemovals](auto it) {
+ return remoteRemovals.contains(it.key(), it.value());
+ });
+ if (MAIN().isDebugEnabled())
+ for (auto it = remoteRemovals.begin();
+ it != remoteRemovals.end(); ++it) {
+ qCDebug(MAIN)
+ << it.value() << "is no more a direct chat with"
+ << it.key()->id();
+ }
+
+ DirectChatsMap remoteAdditions;
+ for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) {
+ if (auto* u = user(it.key())) {
+ if (!d->directChats.contains(u, it.value())
+ && !d->dcLocalRemovals.contains(u, it.value())) {
+ Q_ASSERT(!d->directChatUsers.contains(it.value(), u));
+ remoteAdditions.insert(u, it.value());
+ d->directChats.insert(u, it.value());
+ d->directChatUsers.insert(it.value(), u);
+ qCDebug(MAIN) << "Marked room" << it.value()
+ << "as a direct chat with" << u->id();
+ }
+ } else
+ qCWarning(MAIN)
+ << "Couldn't get a user object for" << it.key();
+ }
+ // Remove from dcLocalAdditions what the server already has.
+ erase_if(d->dcLocalAdditions, [&remoteAdditions](auto it) {
+ return remoteAdditions.contains(it.key(), it.value());
+ });
+ if (!remoteAdditions.isEmpty() || !remoteRemovals.isEmpty())
+ emit directChatsListChanged(remoteAdditions, remoteRemovals);
+ },
+ // catch-all, passing eventPtr for a possible take-over
+ [this, &eventPtr](const Event& accountEvent) {
+ if (is<IgnoredUsersEvent>(accountEvent))
+ qCDebug(MAIN)
+ << "Users ignored by" << d->userId << "updated:"
+ << QStringList::fromSet(ignoredUsers()).join(',');
+
+ auto& currentData = d->accountData[accountEvent.matrixType()];
+ // A polymorphic event-specific comparison might be a bit
+ // more efficient; maaybe do it another day
+ if (!currentData
+ || currentData->contentJson() != accountEvent.contentJson()) {
+ currentData = std::move(eventPtr);
+ qCDebug(MAIN) << "Updated account data of type"
+ << currentData->matrixType();
+ emit accountDataChanged(currentData->matrixType());
+ }
+ });
}
if (!d->dcLocalAdditions.isEmpty() || !d->dcLocalRemovals.isEmpty()) {
qDebug(MAIN) << "Sending updated direct chats to the server:"
@@ -531,8 +514,8 @@ void Connection::onSyncSuccess(SyncData &&data, bool fromCache) {
void Connection::stopSync()
{
// If there's a sync loop, break it
- disconnect(this, &Connection::syncDone,
- this, &Connection::syncLoopIteration);
+ disconnect(this, &Connection::syncDone, this,
+ &Connection::syncLoopIteration);
if (d->syncJob) // If there's an ongoing sync job, stop it too
{
d->syncJob->abandon();
@@ -540,10 +523,7 @@ void Connection::stopSync()
}
}
-QString Connection::nextBatchToken() const
-{
- return d->data->lastEvent();
-}
+QString Connection::nextBatchToken() const { return d->data->lastEvent(); }
PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event) const
{
@@ -556,8 +536,8 @@ JoinRoomJob* Connection::joinRoom(const QString& roomAlias,
auto job = callApi<JoinRoomJob>(roomAlias, serverNames);
// Upon completion, ensure a room object in Join state is created but only
// if it's not already there due to a sync completing earlier.
- connect(job, &JoinRoomJob::success,
- this, [this, job] { provideRoom(job->roomId()); });
+ connect(job, &JoinRoomJob::success, this,
+ [this, job] { provideRoom(job->roomId()); });
return job;
}
@@ -565,15 +545,13 @@ LeaveRoomJob* Connection::leaveRoom(Room* room)
{
const auto& roomId = room->id();
const auto job = callApi<LeaveRoomJob>(roomId);
- if (room->joinState() == JoinState::Invite)
- {
+ if (room->joinState() == JoinState::Invite) {
// Workaround matrix-org/synapse#2181 - if the room is in invite state
// the invite may have been cancelled but Synapse didn't send it in
// `/sync`. See also #273 for the discussion in the library context.
d->pendingStateRoomIds.push_back(roomId);
- connect(job, &LeaveRoomJob::success, this, [this,roomId] {
- if (d->pendingStateRoomIds.removeOne(roomId))
- {
+ connect(job, &LeaveRoomJob::success, this, [this, roomId] {
+ if (d->pendingStateRoomIds.removeOne(roomId)) {
qCDebug(MAIN) << "Forcing the room to Leave status";
provideRoom(roomId, JoinState::Leave);
}
@@ -586,40 +564,42 @@ inline auto splitMediaId(const QString& mediaId)
{
auto idParts = mediaId.split('/');
Q_ASSERT_X(idParts.size() == 2, __FUNCTION__,
- ("'" + mediaId +
- "' doesn't look like 'serverName/localMediaId'").toLatin1());
+ ("'" + mediaId + "' doesn't look like 'serverName/localMediaId'")
+ .toLatin1());
return idParts;
}
MediaThumbnailJob* Connection::getThumbnail(const QString& mediaId,
- QSize requestedSize, RunningPolicy policy) const
+ QSize requestedSize,
+ RunningPolicy policy) const
{
auto idParts = splitMediaId(mediaId);
- return callApi<MediaThumbnailJob>(policy,
- idParts.front(), idParts.back(), requestedSize);
+ return callApi<MediaThumbnailJob>(policy, idParts.front(), idParts.back(),
+ requestedSize);
}
-MediaThumbnailJob* Connection::getThumbnail(const QUrl& url,
- QSize requestedSize, RunningPolicy policy) const
+MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, QSize requestedSize,
+ RunningPolicy policy) const
{
return getThumbnail(url.authority() + url.path(), requestedSize, policy);
}
-MediaThumbnailJob* Connection::getThumbnail(const QUrl& url,
- int requestedWidth, int requestedHeight, RunningPolicy policy) const
+MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, int requestedWidth,
+ int requestedHeight,
+ RunningPolicy policy) const
{
return getThumbnail(url, QSize(requestedWidth, requestedHeight), policy);
}
-UploadContentJob* Connection::uploadContent(QIODevice* contentSource,
- const QString& filename, const QString& overrideContentType) const
+UploadContentJob*
+Connection::uploadContent(QIODevice* contentSource, const QString& filename,
+ const QString& overrideContentType) const
{
auto contentType = overrideContentType;
- if (contentType.isEmpty())
- {
- contentType =
- QMimeDatabase().mimeTypeForFileNameAndData(filename, contentSource)
- .name();
+ if (contentType.isEmpty()) {
+ contentType = QMimeDatabase()
+ .mimeTypeForFileNameAndData(filename, contentSource)
+ .name();
contentSource->open(QIODevice::ReadOnly);
}
return callApi<UploadContentJob>(contentSource, filename, contentType);
@@ -629,8 +609,7 @@ UploadContentJob* Connection::uploadFile(const QString& fileName,
const QString& overrideContentType)
{
auto sourceFile = new QFile(fileName);
- if (!sourceFile->open(QIODevice::ReadOnly))
- {
+ if (!sourceFile->open(QIODevice::ReadOnly)) {
qCWarning(MAIN) << "Couldn't open" << sourceFile->fileName()
<< "for reading";
return nullptr;
@@ -660,30 +639,32 @@ DownloadFileJob* Connection::downloadFile(const QUrl& url,
return job;
}
-CreateRoomJob* Connection::createRoom(RoomVisibility visibility,
- const QString& alias, const QString& name, const QString& topic,
- QStringList invites, const QString& presetName,
- const QString& roomVersion, bool isDirect,
- const QVector<CreateRoomJob::StateEvent>& initialState,
- const QVector<CreateRoomJob::Invite3pid>& invite3pids,
- const QJsonObject& creationContent)
+CreateRoomJob*
+Connection::createRoom(RoomVisibility visibility, const QString& alias,
+ const QString& name, const QString& topic,
+ QStringList invites, const QString& presetName,
+ const QString& roomVersion, bool isDirect,
+ const QVector<CreateRoomJob::StateEvent>& initialState,
+ const QVector<CreateRoomJob::Invite3pid>& invite3pids,
+ const QJsonObject& creationContent)
{
invites.removeOne(d->userId); // The creator is by definition in the room
- auto job = callApi<CreateRoomJob>(
- visibility == PublishRoom ? QStringLiteral("public")
- : QStringLiteral("private"),
- alias, name, topic, invites, invite3pids, roomVersion,
- creationContent, initialState, presetName, isDirect);
- connect(job, &BaseJob::success, this, [this,job,invites,isDirect] {
+ auto job = callApi<CreateRoomJob>(visibility == PublishRoom
+ ? QStringLiteral("public")
+ : QStringLiteral("private"),
+ alias, name, topic, invites, invite3pids,
+ roomVersion, creationContent,
+ initialState, presetName, isDirect);
+ connect(job, &BaseJob::success, this, [this, job, invites, isDirect] {
auto* room = provideRoom(job->roomId(), JoinState::Join);
- if (!room)
- {
- Q_ASSERT_X(room, "Connection::createRoom", "Failed to create a room");
+ if (!room) {
+ Q_ASSERT_X(room, "Connection::createRoom",
+ "Failed to create a room");
return;
}
emit createdRoom(room);
if (isDirect)
- for (const auto& i: invites)
+ for (const auto& i : invites)
addToDirectChats(room, user(i));
});
return job;
@@ -694,14 +675,14 @@ void Connection::requestDirectChat(const QString& userId)
if (auto* u = user(userId))
requestDirectChat(u);
else
- qCCritical(MAIN)
- << "Connection::requestDirectChat: Couldn't get a user object for"
- << userId;
+ qCCritical(MAIN) << "Connection::requestDirectChat: Couldn't get a "
+ "user object for"
+ << userId;
}
void Connection::requestDirectChat(User* u)
{
- doInDirectChat(u, [this] (Room* r) { emit directChatAvailable(r); });
+ doInDirectChat(u, [this](Room* r) { emit directChatAvailable(r); });
}
void Connection::doInDirectChat(const QString& userId,
@@ -724,11 +705,9 @@ void Connection::doInDirectChat(User* u,
// not left), and delete inexistent (forgotten?) ones along the way.
DirectChatsMap removals;
for (auto it = d->directChats.find(u);
- it != d->directChats.end() && it.key() == u; ++it)
- {
+ it != d->directChats.end() && it.key() == u; ++it) {
const auto& roomId = *it;
- if (auto r = room(roomId, JoinState::Join))
- {
+ if (auto r = room(roomId, JoinState::Join)) {
Q_ASSERT(r->id() == roomId);
// A direct chat with yourself should only involve yourself :)
if (userId == d->userId && r->totalMemberCount() > 1)
@@ -738,15 +717,16 @@ void Connection::doInDirectChat(User* u,
operation(r);
return;
}
- if (auto ir = invitation(roomId))
- {
+ if (auto ir = invitation(roomId)) {
Q_ASSERT(ir->id() == roomId);
auto j = joinRoom(ir->id());
- connect(j, &BaseJob::success, this, [this,roomId,userId,operation] {
- qCDebug(MAIN) << "Joined the already invited direct chat with"
- << userId << "as" << roomId;
- operation(room(roomId, JoinState::Join));
- });
+ connect(j, &BaseJob::success, this,
+ [this, roomId, userId, operation] {
+ qCDebug(MAIN)
+ << "Joined the already invited direct chat with"
+ << userId << "as" << roomId;
+ operation(room(roomId, JoinState::Join));
+ });
return;
}
// Avoid reusing previously left chats but don't remove them
@@ -761,10 +741,8 @@ void Connection::doInDirectChat(User* u,
// Add to the list of updates to send to the server upon the next sync.
d->dcLocalRemovals.insert(it.key(), it.value());
}
- if (!removals.isEmpty())
- {
- for (auto it = removals.cbegin(); it != removals.cend(); ++it)
- {
+ if (!removals.isEmpty()) {
+ for (auto it = removals.cbegin(); it != removals.cend(); ++it) {
d->directChats.remove(it.key(), it.value());
d->directChatUsers.remove(it.value(),
const_cast<User*>(it.key())); // FIXME
@@ -773,18 +751,18 @@ void Connection::doInDirectChat(User* u,
}
auto j = createDirectChat(userId);
- connect(j, &BaseJob::success, this, [this,j,userId,operation] {
- qCDebug(MAIN) << "Direct chat with" << userId
- << "has been created as" << j->roomId();
+ connect(j, &BaseJob::success, this, [this, j, userId, operation] {
+ qCDebug(MAIN) << "Direct chat with" << userId << "has been created as"
+ << j->roomId();
operation(room(j->roomId(), JoinState::Join));
});
-
}
CreateRoomJob* Connection::createDirectChat(const QString& userId,
- const QString& topic, const QString& name)
+ const QString& topic,
+ const QString& name)
{
- return createRoom(UnpublishRoom, {}, name, topic, {userId},
+ return createRoom(UnpublishRoom, {}, name, topic, { userId },
QStringLiteral("trusted_private_chat"), {}, true);
}
@@ -798,17 +776,15 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id)
// a ForgetRoomJob is created in advance and can be returned in a probably
// not-yet-started state (it will start once /leave completes).
auto forgetJob = new ForgetRoomJob(id);
- auto room = d->roomMap.value({id, false});
+ auto room = d->roomMap.value({ id, false });
if (!room)
- room = d->roomMap.value({id, true});
- if (room && room->joinState() != JoinState::Leave)
- {
+ room = d->roomMap.value({ id, true });
+ if (room && room->joinState() != JoinState::Leave) {
auto leaveJob = room->leaveRoom();
connect(leaveJob, &BaseJob::result, this,
[this, leaveJob, forgetJob, room] {
if (leaveJob->error() == BaseJob::Success
- || leaveJob->error() == BaseJob::NotFoundError)
- {
+ || leaveJob->error() == BaseJob::NotFoundError) {
forgetJob->start(connectionData());
// If the matching /sync response hasn't arrived yet,
// mark the room for explicit deletion
@@ -822,14 +798,12 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id)
}
});
connect(leaveJob, &BaseJob::failure, forgetJob, &BaseJob::abandon);
- }
- else
+ } else
forgetJob->start(connectionData());
- connect(forgetJob, &BaseJob::result, this, [this, id, forgetJob]
- {
+ connect(forgetJob, &BaseJob::result, this, [this, id, forgetJob] {
// Leave room in case of success, or room not known by server
- if(forgetJob->error() == BaseJob::Success
- || forgetJob->error() == BaseJob::NotFoundError)
+ if (forgetJob->error() == BaseJob::Success
+ || forgetJob->error() == BaseJob::NotFoundError)
d->removeRoom(id); // Delete the room from roomMap
else
qCWarning(MAIN).nospace() << "Error forgetting room " << id << ": "
@@ -838,57 +812,54 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id)
return forgetJob;
}
-SendToDeviceJob* Connection::sendToDevices(const QString& eventType,
- const UsersToDevicesToEvents& eventsMap) const
+SendToDeviceJob*
+Connection::sendToDevices(const QString& eventType,
+ const UsersToDevicesToEvents& eventsMap) const
{
QHash<QString, QHash<QString, QJsonObject>> json;
json.reserve(int(eventsMap.size()));
std::for_each(eventsMap.begin(), eventsMap.end(),
- [&json] (const auto& userTodevicesToEvents) {
- auto& jsonUser = json[userTodevicesToEvents.first];
- const auto& devicesToEvents = userTodevicesToEvents.second;
- std::for_each(devicesToEvents.begin(), devicesToEvents.end(),
- [&jsonUser] (const auto& deviceToEvents) {
- jsonUser.insert(deviceToEvents.first,
- deviceToEvents.second.contentJson());
- });
- });
- return callApi<SendToDeviceJob>(BackgroundRequest,
- eventType, generateTxnId(), json);
+ [&json](const auto& userTodevicesToEvents) {
+ auto& jsonUser = json[userTodevicesToEvents.first];
+ const auto& devicesToEvents = userTodevicesToEvents.second;
+ std::for_each(devicesToEvents.begin(),
+ devicesToEvents.end(),
+ [&jsonUser](const auto& deviceToEvents) {
+ jsonUser.insert(
+ deviceToEvents.first,
+ deviceToEvents.second.contentJson());
+ });
+ });
+ return callApi<SendToDeviceJob>(BackgroundRequest, eventType,
+ generateTxnId(), json);
}
SendMessageJob* Connection::sendMessage(const QString& roomId,
const RoomEvent& event) const
{
- const auto txnId = event.transactionId().isEmpty()
- ? generateTxnId() : event.transactionId();
- return callApi<SendMessageJob>(roomId, event.matrixType(),
- txnId, event.contentJson());
+ const auto txnId = event.transactionId().isEmpty() ? generateTxnId()
+ : event.transactionId();
+ return callApi<SendMessageJob>(roomId, event.matrixType(), txnId,
+ event.contentJson());
}
-QUrl Connection::homeserver() const
-{
- return d->data->baseUrl();
-}
+QUrl Connection::homeserver() const { return d->data->baseUrl(); }
-QString Connection::domain() const
-{
- return d->userId.section(':', 1);
-}
+QString Connection::domain() const { return d->userId.section(':', 1); }
Room* Connection::room(const QString& roomId, JoinStates states) const
{
- Room* room = d->roomMap.value({roomId, false}, nullptr);
- if (states.testFlag(JoinState::Join) &&
- room && room->joinState() == JoinState::Join)
+ Room* room = d->roomMap.value({ roomId, false }, nullptr);
+ if (states.testFlag(JoinState::Join) && room
+ && room->joinState() == JoinState::Join)
return room;
if (states.testFlag(JoinState::Invite))
if (Room* invRoom = invitation(roomId))
return invRoom;
- if (states.testFlag(JoinState::Leave) &&
- room && room->joinState() == JoinState::Leave)
+ if (states.testFlag(JoinState::Leave) && room
+ && room->joinState() == JoinState::Leave)
return room;
return nullptr;
@@ -896,8 +867,7 @@ Room* Connection::room(const QString& roomId, JoinStates states) const
Room* Connection::roomByAlias(const QString& roomAlias, JoinStates states) const
{
- const auto id =
- d->roomAliasMap.value(serverPart(roomAlias)).value(roomAlias);
+ const auto id = d->roomAliasMap.value(serverPart(roomAlias)).value(roomAlias);
if (!id.isEmpty())
return room(id, states);
@@ -912,18 +882,16 @@ void Connection::updateRoomAliases(const QString& roomId,
const QStringList& roomAliases)
{
auto& aliasMap = d->roomAliasMap[aliasServer]; // Allocate if necessary
- for (const auto& a: previousRoomAliases)
+ for (const auto& a : previousRoomAliases)
if (aliasMap.remove(a) == 0)
qCWarning(MAIN) << "Alias" << a << "is not found (already deleted?)";
- for (const auto& a: roomAliases)
- {
+ for (const auto& a : roomAliases) {
auto& mappedId = aliasMap[a];
- if (!mappedId.isEmpty())
- {
+ if (!mappedId.isEmpty()) {
if (mappedId == roomId)
- qCDebug(MAIN) << "Alias" << a << "is already mapped to"
- << roomId;
+ qCDebug(MAIN)
+ << "Alias" << a << "is already mapped to" << roomId;
else
qCWarning(MAIN) << "Alias" << a << "will be force-remapped from"
<< mappedId << "to" << roomId;
@@ -934,19 +902,18 @@ void Connection::updateRoomAliases(const QString& roomId,
Room* Connection::invitation(const QString& roomId) const
{
- return d->roomMap.value({roomId, true}, nullptr);
+ return d->roomMap.value({ roomId, true }, nullptr);
}
User* Connection::user(const QString& userId)
{
if (userId.isEmpty())
return nullptr;
- if (!userId.startsWith('@') || !userId.contains(':'))
- {
+ if (!userId.startsWith('@') || !userId.contains(':')) {
qCCritical(MAIN) << "Malformed userId:" << userId;
return nullptr;
}
- if( d->userMap.contains(userId) )
+ if (d->userMap.contains(userId))
return d->userMap.value(userId);
auto* user = userFactory()(this, userId);
d->userMap.insert(userId, user);
@@ -959,47 +926,32 @@ const User* Connection::user() const
return d->userMap.value(d->userId, nullptr);
}
-User* Connection::user()
-{
- return user(d->userId);
-}
+User* Connection::user() { return user(d->userId); }
-QString Connection::userId() const
-{
- return d->userId;
-}
+QString Connection::userId() const { return d->userId; }
-QString Connection::deviceId() const
-{
- return d->data->deviceId();
-}
+QString Connection::deviceId() const { return d->data->deviceId(); }
-QByteArray Connection::accessToken() const
-{
- return d->data->accessToken();
-}
+QByteArray Connection::accessToken() const { return d->data->accessToken(); }
QtOlm::Account* Connection::olmAccount() const
{
return d->encryptionManager->account();
}
-SyncJob* Connection::syncJob() const
-{
- return d->syncJob;
-}
+SyncJob* Connection::syncJob() const { return d->syncJob; }
int Connection::millisToReconnect() const
{
return d->syncJob ? d->syncJob->millisToRetry() : 0;
}
-QHash< QPair<QString, bool>, Room* > Connection::roomMap() const
+QHash<QPair<QString, bool>, Room*> Connection::roomMap() const
{
- // Copy-on-write-and-remove-elements is faster than copying elements one by one.
- QHash< QPair<QString, bool>, Room* > roomMap = d->roomMap;
- for (auto it = roomMap.begin(); it != roomMap.end(); )
- {
+ // Copy-on-write-and-remove-elements is faster than copying elements one by
+ // one.
+ QHash<QPair<QString, bool>, Room*> roomMap = d->roomMap;
+ for (auto it = roomMap.begin(); it != roomMap.end();) {
if (it.value()->joinState() == JoinState::Leave)
it = roomMap.erase(it);
else
@@ -1039,27 +991,24 @@ void Connection::setAccountData(const QString& type, const QJsonObject& content)
QHash<QString, QVector<Room*>> Connection::tagsToRooms() const
{
QHash<QString, QVector<Room*>> result;
- for (auto* r: qAsConst(d->roomMap))
- {
+ for (auto* r : qAsConst(d->roomMap)) {
const auto& tagNames = r->tagNames();
- for (const auto& tagName: tagNames)
+ for (const auto& tagName : tagNames)
result[tagName].push_back(r);
}
for (auto it = result.begin(); it != result.end(); ++it)
- std::sort(it->begin(), it->end(),
- [t=it.key()] (Room* r1, Room* r2) {
- return r1->tags().value(t) < r2->tags().value(t);
- });
+ std::sort(it->begin(), it->end(), [t = it.key()](Room* r1, Room* r2) {
+ return r1->tags().value(t) < r2->tags().value(t);
+ });
return result;
}
QStringList Connection::tagNames() const
{
- QStringList tags ({FavouriteTag});
- for (auto* r: qAsConst(d->roomMap))
- {
+ QStringList tags({ FavouriteTag });
+ for (auto* r : qAsConst(d->roomMap)) {
const auto& tagNames = r->tagNames();
- for (const auto& tag: tagNames)
+ for (const auto& tag : tagNames)
if (tag != LowPriorityTag && !tags.contains(tag))
tags.push_back(tag);
}
@@ -1071,7 +1020,7 @@ QVector<Room*> Connection::roomsWithTag(const QString& tagName) const
{
QVector<Room*> rooms;
std::copy_if(d->roomMap.begin(), d->roomMap.end(), std::back_inserter(rooms),
- [&tagName] (Room* r) { return r->tags().contains(tagName); });
+ [&tagName](Room* r) { return r->tags().contains(tagName); });
return rooms;
}
@@ -1083,12 +1032,10 @@ Connection::DirectChatsMap Connection::directChats() const
// Removes room with given id from roomMap
void Connection::Private::removeRoom(const QString& roomId)
{
- for (auto f: {false, true})
- if (auto r = roomMap.take({ roomId, f }))
- {
- qCDebug(MAIN) << "Room" << r->objectName()
- << "in state" << toCString(r->joinState())
- << "will be deleted";
+ for (auto f : { false, true })
+ if (auto r = roomMap.take({ roomId, f })) {
+ qCDebug(MAIN) << "Room" << r->objectName() << "in state"
+ << toCString(r->joinState()) << "will be deleted";
emit r->beforeDestruction(r);
r->deleteLater();
}
@@ -1109,20 +1056,19 @@ void Connection::addToDirectChats(const Room* room, User* user)
void Connection::removeFromDirectChats(const QString& roomId, User* user)
{
Q_ASSERT(!roomId.isEmpty());
- if ((user != nullptr && !d->directChats.contains(user, roomId)) ||
- d->directChats.key(roomId) == nullptr)
+ if ((user != nullptr && !d->directChats.contains(user, roomId))
+ || d->directChats.key(roomId) == nullptr)
return;
DirectChatsMap removals;
- if (user != nullptr)
- {
+ if (user != nullptr) {
d->directChats.remove(user, roomId);
d->directChatUsers.remove(roomId, user);
removals.insert(user, roomId);
d->dcLocalRemovals.insert(user, roomId);
} else {
removals = erase_if(d->directChats,
- [&roomId] (auto it) { return it.value() == roomId; });
+ [&roomId](auto it) { return it.value() == roomId; });
d->directChatUsers.remove(roomId);
d->dcLocalRemovals += removals;
}
@@ -1156,11 +1102,10 @@ void Connection::addToIgnoredUsers(const User* user)
Q_ASSERT(user != nullptr);
auto ignoreList = ignoredUsers();
- if (!ignoreList.contains(user->id()))
- {
+ if (!ignoreList.contains(user->id())) {
ignoreList.insert(user->id());
d->packAndSendAccountData<IgnoredUsersEvent>(ignoreList);
- emit ignoredUsersListChanged({{ user->id() }}, {});
+ emit ignoredUsersListChanged({ { user->id() } }, {});
}
}
@@ -1169,17 +1114,13 @@ void Connection::removeFromIgnoredUsers(const User* user)
Q_ASSERT(user != nullptr);
auto ignoreList = ignoredUsers();
- if (ignoreList.remove(user->id()) != 0)
- {
+ if (ignoreList.remove(user->id()) != 0) {
d->packAndSendAccountData<IgnoredUsersEvent>(ignoreList);
- emit ignoredUsersListChanged({}, {{ user->id() }});
+ emit ignoredUsersListChanged({}, { { user->id() } });
}
}
-QMap<QString, User*> Connection::users() const
-{
- return d->userMap;
-}
+QMap<QString, User*> Connection::users() const { return d->userMap; }
const ConnectionData* Connection::connectionData() const
{
@@ -1194,61 +1135,55 @@ Room* Connection::provideRoom(const QString& id, Omittable<JoinState> joinState)
// If joinState.omitted(), all joinState == comparisons below are false.
const auto roomKey = qMakePair(id, joinState == JoinState::Invite);
auto* room = d->roomMap.value(roomKey, nullptr);
- if (room)
- {
+ if (room) {
// Leave is a special case because in transition (5a) (see the .h file)
// joinState == room->joinState but we still have to preempt the Invite
// and emit a signal. For Invite and Join, there's no such problem.
if (room->joinState() == joinState && joinState != JoinState::Leave)
return room;
- } else if (joinState.omitted())
- {
+ } else if (joinState.omitted()) {
// No Join and Leave, maybe Invite?
- room = d->roomMap.value({id, true}, nullptr);
+ room = d->roomMap.value({ id, true }, nullptr);
if (room)
return room;
// No Invite either, setup a new room object below
}
- if (!room)
- {
+ if (!room) {
room = roomFactory()(this, id,
- joinState.omitted() ? JoinState::Join : joinState.value());
- if (!room)
- {
+ joinState.omitted() ? JoinState::Join
+ : joinState.value());
+ if (!room) {
qCCritical(MAIN) << "Failed to create a room" << id;
return nullptr;
}
d->roomMap.insert(roomKey, room);
d->firstTimeRooms.push_back(room);
- connect(room, &Room::beforeDestruction,
- this, &Connection::aboutToDeleteRoom);
+ connect(room, &Room::beforeDestruction, this,
+ &Connection::aboutToDeleteRoom);
emit newRoom(room);
}
if (joinState.omitted())
return room;
- if (joinState == JoinState::Invite)
- {
+ if (joinState == JoinState::Invite) {
// prev is either Leave or nullptr
- auto* prev = d->roomMap.value({id, false}, nullptr);
+ auto* prev = d->roomMap.value({ id, false }, nullptr);
emit invitedRoom(room, prev);
- }
- else
- {
+ } else {
room->setJoinState(joinState.value());
// Preempt the Invite room (if any) with a room in Join/Leave state.
- auto* prevInvite = d->roomMap.take({id, true});
+ auto* prevInvite = d->roomMap.take({ id, true });
if (joinState == JoinState::Join)
emit joinedRoom(room, prevInvite);
else if (joinState == JoinState::Leave)
emit leftRoom(room, prevInvite);
- if (prevInvite)
- {
+ if (prevInvite) {
const auto dcUsers = prevInvite->directChatUsers();
- for (auto* u: dcUsers)
+ for (auto* u : dcUsers)
addToDirectChats(room, u);
- qCDebug(MAIN) << "Deleting Invite state for room" << prevInvite->id();
+ qCDebug(MAIN) << "Deleting Invite state for room"
+ << prevInvite->id();
emit prevInvite->beforeDestruction(prevInvite);
prevInvite->deleteLater();
}
@@ -1267,15 +1202,9 @@ void Connection::setUserFactory(user_factory_t f)
_userFactory = std::move(f);
}
-room_factory_t Connection::roomFactory()
-{
- return _roomFactory;
-}
+room_factory_t Connection::roomFactory() { return _roomFactory; }
-user_factory_t Connection::userFactory()
-{
- return _userFactory;
-}
+user_factory_t Connection::userFactory() { return _userFactory; }
room_factory_t Connection::_roomFactory = defaultRoomFactory<>();
user_factory_t Connection::_userFactory = defaultUserFactory<>();
@@ -1300,18 +1229,17 @@ void Connection::saveRoomState(Room* r) const
if (!d->cacheState)
return;
- QFile outRoomFile {
- stateCacheDir().filePath(SyncData::fileNameForRoom(r->id())) };
- if (outRoomFile.open(QFile::WriteOnly))
- {
+ QFile outRoomFile { stateCacheDir().filePath(
+ SyncData::fileNameForRoom(r->id())) };
+ if (outRoomFile.open(QFile::WriteOnly)) {
QJsonDocument json { r->toJson() };
auto data = d->cacheToBinary ? json.toBinaryData()
: json.toJson(QJsonDocument::Compact);
outRoomFile.write(data.data(), data.size());
qCDebug(MAIN) << "Room state cache saved to" << outRoomFile.fileName();
} else {
- qCWarning(MAIN) << "Error opening" << outRoomFile.fileName()
- << ":" << outRoomFile.errorString();
+ qCWarning(MAIN) << "Error opening" << outRoomFile.fileName() << ":"
+ << outRoomFile.errorString();
}
}
@@ -1320,30 +1248,31 @@ void Connection::saveState() const
if (!d->cacheState)
return;
- QElapsedTimer et; et.start();
+ QElapsedTimer et;
+ et.start();
QFile outFile { d->topLevelStatePath() };
- if (!outFile.open(QFile::WriteOnly))
- {
- qCWarning(MAIN) << "Error opening" << outFile.fileName()
- << ":" << outFile.errorString();
+ if (!outFile.open(QFile::WriteOnly)) {
+ qCWarning(MAIN) << "Error opening" << outFile.fileName() << ":"
+ << outFile.errorString();
qCWarning(MAIN) << "Caching the rooms state disabled";
d->cacheState = false;
return;
}
QJsonObject rootObj {
- { QStringLiteral("cache_version"), QJsonObject {
- { QStringLiteral("major"), SyncData::cacheVersion().first },
- { QStringLiteral("minor"), SyncData::cacheVersion().second }
- }}};
+ { QStringLiteral("cache_version"),
+ QJsonObject {
+ { QStringLiteral("major"), SyncData::cacheVersion().first },
+ { QStringLiteral("minor"), SyncData::cacheVersion().second } } }
+ };
{
QJsonObject rooms;
QJsonObject inviteRooms;
const auto& rs = roomMap(); // Pass on rooms in Leave state
for (const auto* i : rs)
(i->joinState() == JoinState::Invite ? inviteRooms : rooms)
- .insert(i->id(), QJsonValue::Null);
+ .insert(i->id(), QJsonValue::Null);
QJsonObject roomObj;
if (!rooms.isEmpty())
@@ -1358,17 +1287,18 @@ void Connection::saveState() const
QJsonArray accountDataEvents {
basicEventJson(QStringLiteral("m.direct"), toJson(d->directChats))
};
- for (const auto &e : d->accountData)
+ for (const auto& e : d->accountData)
accountDataEvents.append(
basicEventJson(e.first, e.second->contentJson()));
rootObj.insert(QStringLiteral("account_data"),
- QJsonObject {{ QStringLiteral("events"), accountDataEvents }});
+ QJsonObject {
+ { QStringLiteral("events"), accountDataEvents } });
}
QJsonDocument json { rootObj };
- auto data = d->cacheToBinary ? json.toBinaryData() :
- json.toJson(QJsonDocument::Compact);
+ auto data = d->cacheToBinary ? json.toBinaryData()
+ : json.toJson(QJsonDocument::Compact);
qCDebug(PROFILER) << "Cache for" << userId() << "generated in" << et;
outFile.write(data.data(), data.size());
@@ -1380,14 +1310,14 @@ void Connection::loadState()
if (!d->cacheState)
return;
- QElapsedTimer et; et.start();
+ QElapsedTimer et;
+ et.start();
SyncData sync { d->topLevelStatePath() };
if (sync.nextBatch().isEmpty()) // No token means no cache by definition
return;
- if (!sync.unresolvedRooms().isEmpty())
- {
+ if (!sync.unresolvedRooms().isEmpty()) {
qCWarning(MAIN) << "State cache incomplete, discarding";
return;
}
@@ -1410,29 +1340,21 @@ QDir Connection::stateCacheDir() const
return cacheLocation(safeUserId);
}
-bool Connection::cacheState() const
-{
- return d->cacheState;
-}
+bool Connection::cacheState() const { return d->cacheState; }
void Connection::setCacheState(bool newValue)
{
- if (d->cacheState != newValue)
- {
+ if (d->cacheState != newValue) {
d->cacheState = newValue;
emit cacheStateChanged();
}
}
-bool QMatrixClient::Connection::lazyLoading() const
-{
- return d->lazyLoading;
-}
+bool QMatrixClient::Connection::lazyLoading() const { return d->lazyLoading; }
void QMatrixClient::Connection::setLazyLoading(bool newValue)
{
- if (d->lazyLoading != newValue)
- {
+ if (d->lazyLoading != newValue) {
d->lazyLoading = newValue;
emit lazyLoadingChanged();
}
@@ -1441,12 +1363,12 @@ void QMatrixClient::Connection::setLazyLoading(bool newValue)
void Connection::getTurnServers()
{
auto job = callApi<GetTurnServerJob>();
- connect(job, &GetTurnServerJob::success,
- this, [=] { emit turnServersChanged(job->data()); });
+ connect(job, &GetTurnServerJob::success, this,
+ [=] { emit turnServersChanged(job->data()); });
}
const QString Connection::SupportedRoomVersion::StableTag =
- QStringLiteral("stable");
+ QStringLiteral("stable");
QString Connection::defaultRoomVersion() const
{