From ff020f3b7b95bbdcff7c98c54b84a6d8de38c149 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 26 Nov 2020 10:45:30 +0100 Subject: Room: fix breakage in internal member map The change in 39830496 led to prev_content becoming a fallback not only for displaying user names but also for storing them in the internal member map, which is really not what was intended. A lot of debug logging has been added - this will be moved to a new logging category before merging. --- lib/room.cpp | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index 1af294a7..5992a3ae 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -192,7 +192,7 @@ public: // void inviteUser(User* u); // We might get it at some point in time. void insertMemberIntoMap(User* u); - void removeMemberFromMap(const QString& username, User* u); + void removeMemberFromMap(User* u); // This updates the room displayname field (which is the way a room // should be shown in the room list); called whenever the list of @@ -1354,13 +1354,22 @@ Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) void Room::Private::insertMemberIntoMap(User* u) { - const auto userName = u->name(q); + const auto userName = + getCurrentState(u->id())->displayName(); + qDebug(STATE) << "insertMemberIntoMap(), user" << u->id() << "with name" + << userName; // If there is exactly one namesake of the added user, signal member // renaming for that other one because the two should be disambiguated now. const auto namesakes = membersMap.values(userName); + qDebug(STATE) << namesakes.size() << "namesake(s) found"; // Callers should check they are not adding an existing user once more. Q_ASSERT(!namesakes.contains(u)); + if (namesakes.contains(u)) { // Release version whines but continues + qCCritical(STATE) << "Trying to add a user" << u->id() << "to room" + << q->objectName() << "but that's already in it"; + return; + } if (namesakes.size() == 1) emit q->memberAboutToRename(namesakes.front(), @@ -1370,16 +1379,22 @@ void Room::Private::insertMemberIntoMap(User* u) emit q->memberRenamed(namesakes.front()); } -void Room::Private::removeMemberFromMap(const QString& username, User* u) +void Room::Private::removeMemberFromMap(User* u) { + const auto userName = + getCurrentState(u->id())->displayName(); + + qDebug(STATE) << "removeMemberFromMap(), username" << userName << "for user" + << u->id(); User* namesake = nullptr; - auto namesakes = membersMap.values(username); + auto namesakes = membersMap.values(userName); if (namesakes.size() == 2) { namesake = namesakes.front() == u ? namesakes.back() : namesakes.front(); Q_ASSERT_X(namesake != u, __FUNCTION__, "Room members list is broken"); - emit q->memberAboutToRename(namesake, username); + emit q->memberAboutToRename(namesake, userName); } - membersMap.remove(username, u); + const auto removed = membersMap.remove(userName, u); + qDebug(STATE) << "Removed" << removed << "entries"; // If there was one namesake besides the removed user, signal member // renaming for it because it doesn't need to be disambiguated any more. if (namesake) @@ -2465,7 +2480,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) case MembershipType::Join: // rename/avatar change or no-op if (rme.newDisplayName()) { emit memberAboutToRename(u, *rme.newDisplayName()); - d->removeMemberFromMap(u->name(this), u); + d->removeMemberFromMap(u); } break; case MembershipType::Invite: @@ -2473,7 +2488,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) << rme; [[fallthrough]]; default: // whatever the new membership, it's no more Join - d->removeMemberFromMap(u->name(this), u); + d->removeMemberFromMap(u); emit userRemoved(u); } break; @@ -2555,11 +2570,13 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) switch (evt.membership()) { case MembershipType::Join: if (prevMembership != MembershipType::Join) { + qDebug(STATE) << "!Join -> Join"; d->insertMemberIntoMap(u); emit userAdded(u); } else { if (evt.newDisplayName()) { - d->insertMemberIntoMap(u); + qDebug(STATE) << "After renaming"; + d->insertMemberIntoMap(u); emit memberRenamed(u); } if (evt.newAvatarUrl()) @@ -2779,7 +2796,7 @@ QString Room::Private::calculateDisplayname() const const bool localUserIsIn = joinState == JoinState::Join; const bool emptyRoom = membersMap.isEmpty() - || (membersMap.size() == 1 && isLocalUser(*membersMap.begin())); + || (membersMap.size() == 1 && isLocalUser(*membersMap.cbegin())); const bool nonEmptySummary = summary.heroes && !summary.heroes->empty(); auto shortlist = nonEmptySummary ? buildShortlist(*summary.heroes) : !emptyRoom ? buildShortlist(membersMap) -- cgit v1.2.3 From e617f0151df9a5edbefeb2c36d306a2989a278af Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 24 Dec 2020 18:20:04 +0100 Subject: Fix clang-tidy/clazy warnings (cherry picked from commit 0a2acd750a4155969092be674ed3dd9a71b2354f) --- lib/room.cpp | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index 5992a3ae..55e2931e 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -350,7 +350,7 @@ public: */ bool processReplacement(const RoomMessageEvent& newEvent); - void setTags(TagsMap newTags); + void setTags(TagsMap&& newTags); QJsonObject toJson() const; @@ -1073,11 +1073,11 @@ void Room::setTags(TagsMap newTags, ActionScope applyOn) if (propagate) { for (auto* r = this; (r = r->successor(joinStates));) - r->setTags(newTags, ActionScope::ThisRoomOnly); + r->setTags(d->tags, ActionScope::ThisRoomOnly); } } -void Room::Private::setTags(TagsMap newTags) +void Room::Private::setTags(TagsMap&& newTags) { emit q->tagsAboutToChange(); const auto keys = newTags.keys(); @@ -1192,8 +1192,8 @@ QString Room::fileNameToDownload(const QString& eventId) const FileTransferInfo Room::fileTransferInfo(const QString& id) const { - auto infoIt = d->fileTransfers.find(id); - if (infoIt == d->fileTransfers.end()) + const auto infoIt = d->fileTransfers.constFind(id); + if (infoIt == d->fileTransfers.cend()) return {}; // FIXME: Add lib tests to make sure FileTransferInfo::status stays @@ -1222,8 +1222,8 @@ QUrl Room::fileSource(const QString& id) const return url; // No urlToDownload means it's a pending or completed upload. - auto infoIt = d->fileTransfers.find(id); - if (infoIt != d->fileTransfers.end()) + auto infoIt = d->fileTransfers.constFind(id); + if (infoIt != d->fileTransfers.cend()) return QUrl::fromLocalFile(infoIt->localFileInfo.absoluteFilePath()); qCWarning(MAIN) << "File source for identifier" << id << "not found"; @@ -1312,7 +1312,6 @@ void Room::handleRoomKeyEvent(const RoomKeyEvent& roomKeyEvent, Q_UNUSED(roomKeyEvent) Q_UNUSED(senderKey) qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; - return; #else // Quotient_E2EE_ENABLED if (roomKeyEvent.algorithm() != MegolmV1AesSha2AlgoKey) { qCWarning(E2EE) << "Ignoring unsupported algorithm" @@ -1438,7 +1437,7 @@ Room::Private::moveEventsToTimeline(RoomEventsRange events, } const auto insertedSize = (index - baseIndex) * placement; Q_ASSERT(insertedSize == int(events.size())); - return insertedSize; + return Timeline::size_type(insertedSize); } QString Room::memberName(const QString& mxId) const @@ -1654,8 +1653,8 @@ QString Room::retryMessage(const QString& txnId) const auto it = findPendingEvent(txnId); Q_ASSERT(it != d->unsyncedEvents.end()); qCDebug(EVENTS) << "Retrying transaction" << txnId; - const auto& transferIt = d->fileTransfers.find(txnId); - if (transferIt != d->fileTransfers.end()) { + const auto& transferIt = d->fileTransfers.constFind(txnId); + if (transferIt != d->fileTransfers.cend()) { Q_ASSERT(transferIt->isUpload); if (transferIt->status == FileTransferInfo::Completed) { qCDebug(MESSAGES) @@ -1752,7 +1751,8 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, // to enable the preview while the event is pending. uploadFile(txnId, localPath); // Below, the upload job is used as a context object to clean up connections - connect(this, &Room::fileTransferCompleted, d->fileTransfers[txnId].job, + const auto& transferJob = d->fileTransfers.value(txnId).job; + connect(this, &Room::fileTransferCompleted, transferJob, [this, txnId](const QString& id, const QUrl&, const QUrl& mxcUri) { if (id == txnId) { auto it = findPendingEvent(txnId); @@ -1771,7 +1771,7 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, } } }); - connect(this, &Room::fileTransferCancelled, d->fileTransfers[txnId].job, + connect(this, &Room::fileTransferCancelled, transferJob, [this, txnId](const QString& id) { if (id == txnId) { auto it = findPendingEvent(txnId); @@ -1973,8 +1973,8 @@ void Room::uploadFile(const QString& id, const QUrl& localFilename, void Room::downloadFile(const QString& eventId, const QUrl& localFilename) { - auto ongoingTransfer = d->fileTransfers.find(eventId); - if (ongoingTransfer != d->fileTransfers.end() + if (auto ongoingTransfer = d->fileTransfers.constFind(eventId); + ongoingTransfer != d->fileTransfers.cend() && ongoingTransfer->status == FileTransferInfo::Started) { qCWarning(MAIN) << "Transfer for" << eventId << "is ongoing; download won't start"; @@ -2031,8 +2031,8 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename) void Room::cancelFileTransfer(const QString& id) { - auto it = d->fileTransfers.find(id); - if (it == d->fileTransfers.end()) { + const auto it = d->fileTransfers.constFind(id); + if (it == d->fileTransfers.cend()) { qCWarning(MAIN) << "No information on file transfer" << id << "in room" << d->id; return; @@ -2134,8 +2134,8 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) { // Can't use findInTimeline because it returns a const iterator, and // we need to change the underlying TimelineItem. - const auto pIdx = eventsIndex.find(redaction.redactedEvent()); - if (pIdx == eventsIndex.end()) + const auto pIdx = eventsIndex.constFind(redaction.redactedEvent()); + if (pIdx == eventsIndex.cend()) return false; Q_ASSERT(q->isValidIndex(*pIdx)); @@ -2205,8 +2205,8 @@ bool Room::Private::processReplacement(const RoomMessageEvent& newEvent) { // Can't use findInTimeline because it returns a const iterator, and // we need to change the underlying TimelineItem. - const auto pIdx = eventsIndex.find(newEvent.replacedEvent()); - if (pIdx == eventsIndex.end()) + const auto pIdx = eventsIndex.constFind(newEvent.replacedEvent()); + if (pIdx == eventsIndex.cend()) return false; Q_ASSERT(q->isValidIndex(*pIdx)); @@ -2451,12 +2451,11 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (!e.isStateEvent()) return Change::NoChange; - // Find a value (create empty if necessary) and get a reference to it - // getCurrentState<> is not used here because it (creates and) returns + // Find a value (create an empty one if necessary) and get a reference + // to it. Can't use getCurrentState<>() because it (creates and) returns // a stub if a value is not found, and what's needed here is a "real" event // or nullptr. - const auto*& curStateEvent = - d->currentState[{ e.matrixType(), e.stateKey() }]; + auto& curStateEvent = d->currentState[{ e.matrixType(), e.stateKey() }]; // Prepare for the state change visit(e, [this, oldRme = static_cast(curStateEvent)]( const RoomMemberEvent& rme) { -- cgit v1.2.3 From ecaf0093e5857074b51607924035555a4442d4d1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 24 Dec 2020 18:21:41 +0100 Subject: Room: don't accept . at 0-th position in the tag Also: use a structured binding for better code readability. (cherry picked from commit 66972c81d018231f08f3767feda4b41ae5e1b8e0) --- lib/room.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index 55e2931e..a19624d8 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1010,11 +1010,11 @@ TagRecord Room::tag(const QString& name) const { return d->tags.value(name); } std::pair validatedTag(QString name) { - if (name.contains('.')) + if (name.isEmpty() || name.indexOf('.', 1) != -1) return { false, name }; qCWarning(MAIN) << "The tag" << name - << "doesn't follow the CS API conventions"; + << "doesn't follow the CS API conventions"; name.prepend("u."); qCWarning(MAIN) << "Using " << name << "instead"; @@ -1082,11 +1082,11 @@ void Room::Private::setTags(TagsMap&& newTags) emit q->tagsAboutToChange(); const auto keys = newTags.keys(); for (const auto& k : keys) - if (const auto& checkRes = validatedTag(k); checkRes.first) { - if (newTags.contains(checkRes.second)) + if (const auto& [adjusted, adjustedTag] = validatedTag(k); adjusted) { + if (newTags.contains(adjustedTag)) newTags.remove(k); else - newTags.insert(checkRes.second, newTags.take(k)); + newTags.insert(adjustedTag, newTags.take(k)); } tags = move(newTags); -- cgit v1.2.3 From cd9c9296bb1ac7af7ebbbf66931e731dbf581bc8 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Sat, 26 Dec 2020 14:54:31 +0100 Subject: Port existing copyright statement to reuse using licensedigger --- lib/room.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index a19624d8..155f5cd9 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1,19 +1,7 @@ /****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach + * SPDX-FileCopyrightText: 2015 Felix Rohrbach * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "room.h" -- cgit v1.2.3 From 56c1db077b5da653c230432abc6c746318a77bed Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 27 Dec 2020 23:31:20 +0100 Subject: Cleanup and clang-tidy/clazy fixes --- lib/room.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index a19624d8..602653f3 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2522,31 +2522,20 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) , [this, oldStateEvent] (const RoomCanonicalAliasEvent& cae) { // clang-format on setObjectName(cae.alias().isEmpty() ? d->id : cae.alias()); - QString previousCanonicalAlias = - oldStateEvent - ? static_cast(oldStateEvent) - ->alias() - : QString(); - - auto previousAltAliases = - oldStateEvent - ? static_cast(oldStateEvent) - ->altAliases() - : QStringList(); - - if (!previousCanonicalAlias.isEmpty()) { - previousAltAliases.push_back(previousCanonicalAlias); + const auto* oldCae = + static_cast(oldStateEvent); + QStringList previousAltAliases {}; + if (oldCae) { + previousAltAliases = oldCae->altAliases(); + if (!oldCae->alias().isEmpty()) + previousAltAliases.push_back(oldCae->alias()); } - const auto previousAliases = std::move(previousAltAliases); - auto newAliases = cae.altAliases(); - - if (!cae.alias().isEmpty()) { + if (!cae.alias().isEmpty()) newAliases.push_front(cae.alias()); - } - connection()->updateRoomAliases(id(), previousAliases, newAliases); + connection()->updateRoomAliases(id(), previousAltAliases, newAliases); return AliasesChange; // clang-format off } @@ -2588,7 +2577,9 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (u == localUser() && evt.isDirect()) connection()->addToDirectChats(this, user(evt.senderId())); break; - default: + case MembershipType::Knock: + case MembershipType::Ban: + case MembershipType::Leave: if (!d->membersLeft.contains(u)) d->membersLeft.append(u); } -- cgit v1.2.3 From 4bdeacdd332865abf55b94af29f100842ab5d04f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 28 Dec 2020 11:18:05 +0100 Subject: Use generated SetReadMarkerJob instead of PostReadMarkersJob --- lib/room.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index 602653f3..f127b42b 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -36,6 +36,7 @@ #include "csapi/room_state.h" #include "csapi/room_upgrades.h" #include "csapi/rooms.h" +#include "csapi/read_markers.h" #include "csapi/tags.h" #include "events/callanswerevent.h" @@ -55,7 +56,6 @@ #include "events/roompowerlevelsevent.h" #include "jobs/downloadfilejob.h" #include "jobs/mediathumbnailjob.h" -#include "jobs/postreadmarkersjob.h" #include "events/roomcanonicalaliasevent.h" #include @@ -632,8 +632,8 @@ Room::Changes Room::Private::setLastReadEvent(User* u, QString eventId) emit q->readMarkerForUserMoved(u, eventId, storedId); if (isLocalUser(u)) { if (storedId != serverReadMarker) - connection->callApi(BackgroundRequest, id, - storedId); + connection->callApi(BackgroundRequest, id, + storedId); emit q->readMarkerMoved(eventId, storedId); return Change::ReadMarkerChange; } -- cgit v1.2.3 From dcef98f962c29b004d5d9fff1cff0102c6c9768f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 28 Dec 2020 15:53:31 +0100 Subject: Room: really fix namesakes assertion failure This removes now-deprecated RoomMemberEvent API usages and also does a few more things differently from the stable branch. Rather than rely on prev_content (the way pre-0.7 goes), processStateEvent() now includes a pre-check for no-op state events (the fix in ff020f3b turned out to be insufficient for such events and they still caused the same assertion failure at some point down the line). Now the state event is only added to currentState and, where relevant, to baseState, if it actually changes the room state; otherwise, it is ignored for the purpose of state tracking (even when still added to the timeline, if it came in the timeline block). One side-effect of this change is that processStateEvent() now returns OtherChange instead of NoChange for unknown state events. At the same time removeMemberFromMap() now has an additional safety net, making sure that a given user is actually deleted from the map even if their name is mismatched. This comes at a cost of looking through the whole hashmap but normally should not occur with the current code that shaves away no-op state events. We'll only see when some client starts to actively use 0.7 (quotest doesn't trigger those). --- lib/room.cpp | 216 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 134 insertions(+), 82 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index f127b42b..5346c4ff 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -263,10 +263,11 @@ public: for (auto&& eptr : events) { const auto& evt = *eptr; Q_ASSERT(evt.isStateEvent()); - // Update baseState afterwards to make sure that the old state - // is valid and usable inside processStateEvent - changes |= q->processStateEvent(evt); - baseState[{ evt.matrixType(), evt.stateKey() }] = move(eptr); + auto change = q->processStateEvent(evt); + if (change != NoChange) { + changes |= change; + baseState[{ evt.matrixType(), evt.stateKey() }] = move(eptr); + } } if (events.size() > 9 || et.nsecsElapsed() >= profilerMinNsecs()) qCDebug(PROFILER) @@ -1353,23 +1354,27 @@ Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) void Room::Private::insertMemberIntoMap(User* u) { - const auto userName = - getCurrentState(u->id())->displayName(); - qDebug(STATE) << "insertMemberIntoMap(), user" << u->id() << "with name" - << userName; - // If there is exactly one namesake of the added user, signal member - // renaming for that other one because the two should be disambiguated now. + const auto maybeUserName = + getCurrentState(u->id())->newDisplayName(); + if (!maybeUserName) + qCWarning(MEMBERS) << "insertMemberIntoMap():" << u->id() + << "has no name (even empty)"; + const auto userName = maybeUserName.value_or(QString()); const auto namesakes = membersMap.values(userName); - qDebug(STATE) << namesakes.size() << "namesake(s) found"; + qCDebug(MEMBERS) << "insertMemberIntoMap(), user" << u->id() + << "with name" << userName << '-' + << namesakes.size() << "namesake(s) found"; - // Callers should check they are not adding an existing user once more. + // Callers should make sure they are not adding an existing user once more Q_ASSERT(!namesakes.contains(u)); if (namesakes.contains(u)) { // Release version whines but continues - qCCritical(STATE) << "Trying to add a user" << u->id() << "to room" - << q->objectName() << "but that's already in it"; + qCCritical(MEMBERS) << "Trying to add a user" << u->id() << "to room" + << q->objectName() << "but that's already in it"; return; } + // If there is exactly one namesake of the added user, signal member + // renaming for that other one because the two should be disambiguated now if (namesakes.size() == 1) emit q->memberAboutToRename(namesakes.front(), namesakes.front()->fullName(q)); @@ -1381,21 +1386,41 @@ void Room::Private::insertMemberIntoMap(User* u) void Room::Private::removeMemberFromMap(User* u) { const auto userName = - getCurrentState(u->id())->displayName(); + getCurrentState( + u->id())->newDisplayName().value_or(QString()); - qDebug(STATE) << "removeMemberFromMap(), username" << userName << "for user" - << u->id(); + qCDebug(MEMBERS) << "removeMemberFromMap(), username" << userName + << "for user" << u->id(); User* namesake = nullptr; auto namesakes = membersMap.values(userName); + // If there was one namesake besides the removed user, signal member + // renaming for it because it doesn't need to be disambiguated any more. if (namesakes.size() == 2) { - namesake = namesakes.front() == u ? namesakes.back() : namesakes.front(); + namesake = + namesakes.front() == u ? namesakes.back() : namesakes.front(); Q_ASSERT_X(namesake != u, __FUNCTION__, "Room members list is broken"); emit q->memberAboutToRename(namesake, userName); } const auto removed = membersMap.remove(userName, u); - qDebug(STATE) << "Removed" << removed << "entries"; - // If there was one namesake besides the removed user, signal member - // renaming for it because it doesn't need to be disambiguated any more. + if (removed == 0) { + qCDebug(MEMBERS) << "No entries removed; checking the whole list"; + // Unless at the stage of initial filling, this no removed entries + // is suspicious; double-check that this user is not found in + // the whole map, and stop (for debug builds) or shout in the logs + // (for release builds) if there's one. That search is O(n), which + // may come rather expensive for larger rooms. + QElapsedTimer et; + auto it = std::find(membersMap.cbegin(), membersMap.cend(), u); + if (et.nsecsElapsed() > profilerMinNsecs() / 10) + qCDebug(MEMBERS) << "...done in" << et; + if (it != membersMap.cend()) { + Q_ASSERT_X(false, __FUNCTION__, + "Mismatched name in the room members list"); + qCCritical(MEMBERS) << "Mismatched name in the room members list;" + " avoiding the list corruption"; + membersMap.remove(it.key(), u); + } + } if (namesake) emit q->memberRenamed(namesake); } @@ -2449,7 +2474,7 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) Room::Changes Room::processStateEvent(const RoomEvent& e) { if (!e.isStateEvent()) - return Change::NoChange; + return NoChange; // Find a value (create an empty one if necessary) and get a reference // to it. Can't use getCurrentState<>() because it (creates and) returns @@ -2457,48 +2482,87 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) // or nullptr. auto& curStateEvent = d->currentState[{ e.matrixType(), e.stateKey() }]; // Prepare for the state change - visit(e, [this, oldRme = static_cast(curStateEvent)]( - const RoomMemberEvent& rme) { - auto* u = user(rme.userId()); - if (!u) { // ??? - qCCritical(MAIN) - << "Could not get a user object for" << rme.userId(); - return; - } - const auto prevMembership = oldRme ? oldRme->membership() - : MembershipType::Leave; - switch (prevMembership) { - case MembershipType::Invite: - if (rme.membership() != prevMembership) { - d->usersInvited.removeOne(u); - Q_ASSERT(!d->usersInvited.contains(u)); + // clang-format off + const bool proceed = visit(e + , [this, curStateEvent](const RoomMemberEvent& rme) { + // clang-format on + auto* oldRme = static_cast(curStateEvent); + auto* u = user(rme.userId()); + if (!u) { // Some terribly malformed user id? + qCCritical(MAIN) << "Could not get a user object for" + << rme.userId(); + return false; // Stay low and hope for the best... } - break; - case MembershipType::Join: - switch (rme.membership()) { - case MembershipType::Join: // rename/avatar change or no-op - if (rme.newDisplayName()) { - emit memberAboutToRename(u, *rme.newDisplayName()); + const auto prevMembership = oldRme ? oldRme->membership() + : MembershipType::Leave; + switch (prevMembership) { + case MembershipType::Invite: + if (rme.membership() != prevMembership) { + d->usersInvited.removeOne(u); + Q_ASSERT(!d->usersInvited.contains(u)); + } + break; + case MembershipType::Join: + if (rme.membership() == MembershipType::Join) { + // rename/avatar change or no-op + if (rme.newDisplayName()) { + emit memberAboutToRename(u, *rme.newDisplayName()); + d->removeMemberFromMap(u); + } + if (!rme.newDisplayName() && !rme.newAvatarUrl()) { + qCWarning(MEMBERS) + << "No-op membership event for" << rme.userId() + << "- retaining the state"; + qCWarning(MEMBERS) << "The event dump:" << rme; + return false; + } + } else { + if (rme.membership() == MembershipType::Invite) + qCWarning(MAIN) + << "Membership change from Join to Invite:" << rme; + // whatever the new membership, it's no more Join d->removeMemberFromMap(u); + emit userRemoved(u); } break; - case MembershipType::Invite: - qCWarning(MAIN) << "Membership change from Join to Invite:" - << rme; - [[fallthrough]]; - default: // whatever the new membership, it's no more Join - d->removeMemberFromMap(u); - emit userRemoved(u); + case MembershipType::Ban: + case MembershipType::Knock: + case MembershipType::Leave: + if (rme.membership() == MembershipType::Invite + || rme.membership() == MembershipType::Join) { + d->membersLeft.removeOne(u); + Q_ASSERT(!d->membersLeft.contains(u)); + } + break; + case MembershipType::Undefined: + ; // A warning will be dropped in the post-processing block below } - break; - default: - if (rme.membership() == MembershipType::Invite - || rme.membership() == MembershipType::Join) { - d->membersLeft.removeOne(u); - Q_ASSERT(!d->membersLeft.contains(u)); + return true; + // clang-format off + } + , [this, curStateEvent]( const EncryptionEvent& ee) { + // clang-format on + auto* oldEncEvt = + static_cast(curStateEvent); + if (ee.algorithm().isEmpty()) { + qWarning(STATE) + << "The encryption event for room" << objectName() + << "doesn't have 'algorithm' specified - ignoring"; + return false; + } + if (oldEncEvt + && oldEncEvt->encryption() != EncryptionEventContent::Undefined) { + qCWarning(STATE) << "The room is already encrypted but a new" + " room encryption event arrived - ignoring"; + return false; } + return true; + // clang-format off } - }); + , true); // By default, go forward with the state change + // clang-format on + if (!proceed) + return NoChange; // Change the state const auto* const oldStateEvent = @@ -2506,19 +2570,18 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) Q_ASSERT(!oldStateEvent || (oldStateEvent->matrixType() == e.matrixType() && oldStateEvent->stateKey() == e.stateKey())); - if (!is(e)) // Room member events are too numerous + if (is(e)) + qCDebug(MEMBERS) << "Updated room member state:" << e; + else qCDebug(STATE) << "Updated room state:" << e; // Update internal structures as per the change and work out the return value // clang-format off - return visit(e + const auto result = visit(e , [] (const RoomNameEvent&) { return NameChange; } - , [] (const RoomAliasesEvent&) { - return NoChange; // This event has been removed by MSC2432 - } , [this, oldStateEvent] (const RoomCanonicalAliasEvent& cae) { // clang-format on setObjectName(cae.alias().isEmpty() ? d->id : cae.alias()); @@ -2558,13 +2621,11 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) switch (evt.membership()) { case MembershipType::Join: if (prevMembership != MembershipType::Join) { - qDebug(STATE) << "!Join -> Join"; d->insertMemberIntoMap(u); emit userAdded(u); } else { if (evt.newDisplayName()) { - qDebug(STATE) << "After renaming"; - d->insertMemberIntoMap(u); + d->insertMemberIntoMap(u); emit memberRenamed(u); } if (evt.newAvatarUrl()) @@ -2582,30 +2643,18 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) case MembershipType::Leave: if (!d->membersLeft.contains(u)) d->membersLeft.append(u); + break; + case MembershipType::Undefined: + qCWarning(MEMBERS) << "Ignored undefined membership type"; } return MembersChange; // clang-format off } - , [this, oldEncEvt = static_cast(oldStateEvent)]( - const EncryptionEvent& ee) { - // clang-format on - if (ee.algorithm().isEmpty()) { - qWarning(STATE) - << "The encryption event for room" << objectName() - << "doesn't have 'algorithm' specified - ignoring"; - return NoChange; - } - if (oldEncEvt - && oldEncEvt->encryption() != EncryptionEventContent::Undefined) { - qCWarning(STATE) << "The room is already encrypted but a new" - " room encryption event arrived - ignoring"; - return NoChange; - } + , [this] (const EncryptionEvent&) { // As encryption can only be switched on once, emit the signal here // instead of aggregating and emitting in updateData() emit encryption(); return OtherChange; - // clang-format off } , [this] (const RoomTombstoneEvent& evt) { const auto successorId = evt.successorRoomId(); @@ -2622,9 +2671,12 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) }); return OtherChange; + // clang-format off } - ); + , OtherChange); // clang-format on + Q_ASSERT(result != NoChange); + return result; } Room::Changes Room::processEphemeralEvent(EventPtr&& event) -- cgit v1.2.3 From a49c3f2eb4e1aa5c6687c7637c1a06fcd69b0a23 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 28 Dec 2020 16:02:00 +0100 Subject: Room: inline a once-used variable --- lib/room.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index 5346c4ff..a9b2ba30 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1401,8 +1401,7 @@ void Room::Private::removeMemberFromMap(User* u) Q_ASSERT_X(namesake != u, __FUNCTION__, "Room members list is broken"); emit q->memberAboutToRename(namesake, userName); } - const auto removed = membersMap.remove(userName, u); - if (removed == 0) { + if (membersMap.remove(userName, u) == 0) { qCDebug(MEMBERS) << "No entries removed; checking the whole list"; // Unless at the stage of initial filling, this no removed entries // is suspicious; double-check that this user is not found in -- cgit v1.2.3 From 78a3137920d9680072dc3796dd90f849e8467fd4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 7 Jan 2021 21:32:07 +0100 Subject: isJobRunning() -> isJobPending() To be very clear what this function checks. See also #437. --- lib/room.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'lib/room.cpp') diff --git a/lib/room.cpp b/lib/room.cpp index a9b2ba30..5e71881a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -858,7 +858,7 @@ const Room::RelatedEvents Room::relatedEvents(const RoomEvent& evt, void Room::Private::getAllMembers() { // If already loaded or already loading, there's nothing to do here. - if (q->joinedCount() <= membersMap.size() || isJobRunning(allMembersJob)) + if (q->joinedCount() <= membersMap.size() || isJobPending(allMembersJob)) return; allMembersJob = connection->callApi( @@ -1685,7 +1685,7 @@ QString Room::retryMessage(const QString& txnId) << "File for transaction" << txnId << "has already been uploaded, bypassing re-upload"; } else { - if (isJobRunning(transferIt->job)) { + if (isJobPending(transferIt->job)) { qCDebug(MESSAGES) << "Abandoning the upload job for transaction" << txnId << "and starting again"; transferIt->job->abandon(); @@ -1718,7 +1718,7 @@ void Room::discardMessage(const QString& txnId) const auto& transferIt = d->fileTransfers.find(txnId); if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); - if (isJobRunning(transferIt->job)) { + if (isJobPending(transferIt->job)) { transferIt->status = FileTransferInfo::Cancelled; transferIt->job->abandon(); emit fileTransferFailed(txnId, tr("File upload cancelled")); @@ -1924,7 +1924,7 @@ void Room::getPreviousContent(int limit, const QString &filter) { d->getPrevious void Room::Private::getPreviousContent(int limit, const QString &filter) { - if (isJobRunning(eventsHistoryJob)) + if (isJobPending(eventsHistoryJob)) return; eventsHistoryJob = @@ -1977,7 +1977,7 @@ void Room::uploadFile(const QString& id, const QUrl& localFilename, "localFilename should point at a local file"); auto fileName = localFilename.toLocalFile(); auto job = connection()->uploadFile(fileName, overrideContentType); - if (isJobRunning(job)) { + if (isJobPending(job)) { d->fileTransfers[id] = { job, fileName, true }; connect(job, &BaseJob::uploadProgress, this, [this, id](qint64 sent, qint64 total) { @@ -2033,7 +2033,7 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename) qDebug(MAIN) << "File path:" << filePath; } auto job = connection()->downloadFile(fileUrl, filePath); - if (isJobRunning(job)) { + if (isJobPending(job)) { // If there was a previous transfer (completed or failed), overwrite it. d->fileTransfers[eventId] = { job, job->targetFileName() }; connect(job, &BaseJob::downloadProgress, this, @@ -2061,7 +2061,7 @@ void Room::cancelFileTransfer(const QString& id) << d->id; return; } - if (isJobRunning(it->job)) + if (isJobPending(it->job)) it->job->abandon(); d->fileTransfers.remove(id); emit fileTransferCancelled(id); -- cgit v1.2.3