diff options
Diffstat (limited to 'lib/room.cpp')
-rw-r--r-- | lib/room.cpp | 113 |
1 files changed, 66 insertions, 47 deletions
diff --git a/lib/room.cpp b/lib/room.cpp index c6376a26..b0be288b 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -72,14 +72,6 @@ using std::llround; enum EventsPlacement : int { Older = -1, Newer = 1 }; -// A workaround for MSVC 2015 and older GCC's that don't handle initializer -// lists right (MSVC 2015, notably, fails with "error C2440: 'return': -// cannot convert from 'initializer list' to 'QMatrixClient::FileTransferInfo'") -#if (defined(_MSC_VER) && _MSC_VER < 1910) || \ - (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) -# define WORKAROUND_EXTENDED_INITIALIZER_LIST -#endif - class Room::Private { public: @@ -209,6 +201,28 @@ class Room::Private is<RoomMessageEvent>(*ti); } + template <typename EventArrayT> + Changes updateStateFrom(EventArrayT&& events) + { + Changes changes = NoChange; + if (!events.empty()) + { + QElapsedTimer et; et.start(); + 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); + } + if (events.size() > 9 || et.nsecsElapsed() >= profilerMinNsecs()) + qCDebug(PROFILER) << "*** Room::Private::updateStateFrom():" + << events.size() << "event(s)," << et; + } + return changes; + } Changes addNewMessageEvents(RoomEvents&& events); void addHistoricalMessageEvents(RoomEvents&& events); @@ -684,13 +698,7 @@ void Room::Private::getAllMembers() auto nextIndex = timeline.empty() ? 0 : timeline.back().index() + 1; connect( allMembersJob, &BaseJob::success, q, [=] { Q_ASSERT(timeline.empty() || nextIndex <= q->maxTimelineIndex() + 1); - Changes roomChanges = NoChange; - for (auto&& e: allMembersJob->chunk()) - { - const auto& evt = *e; - baseState[{evt.matrixType(),evt.stateKey()}] = move(e); - roomChanges |= q->processStateEvent(evt); - } + auto roomChanges = updateStateFrom(allMembersJob->chunk()); // Replay member events that arrived after the point for which // the full members list was requested. if (!timeline.empty() ) @@ -1049,7 +1057,7 @@ FileTransferInfo Room::fileTransferInfo(const QString& id) const total = INT_MAX; } -#ifdef WORKAROUND_EXTENDED_INITIALIZER_LIST +#ifdef BROKEN_INITIALIZER_LISTS FileTransferInfo fti; fti.status = infoIt->status; fti.progress = int(progress); @@ -1296,21 +1304,8 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) for (auto&& event: data.accountData) roomChanges |= processAccountDataEvent(move(event)); - if (!data.state.empty()) - { - et.restart(); - for (auto&& eptr: data.state) - { - const auto& evt = *eptr; - Q_ASSERT(evt.isStateEvent()); - d->baseState[{evt.matrixType(),evt.stateKey()}] = move(eptr); - roomChanges |= processStateEvent(evt); - } + roomChanges |= d->updateStateFrom(data.state); - if (data.state.size() > 9 || et.nsecsElapsed() >= profilerMinNsecs()) - qCDebug(PROFILER) << "*** Room::processStateEvents():" - << data.state.size() << "event(s)," << et; - } if (!data.timeline.empty()) { et.restart(); @@ -1366,8 +1361,6 @@ RoomEvent* Room::Private::addAsPending(RoomEventPtr&& event) event->setTransactionId(connection->generateTxnId()); auto* pEvent = rawPtr(event); emit q->pendingEventAboutToAdd(pEvent); - // FIXME: This sometimes causes a bad read: - // https://travis-ci.org/QMatrixClient/libqmatrixclient/jobs/492156899#L2596 unsyncedEvents.emplace_back(move(event)); emit q->pendingEventAdded(); return pEvent; @@ -1522,7 +1515,7 @@ QString Room::postPlainText(const QString& plainText) } QString Room::postHtmlMessage(const QString& plainText, const QString& html, - MessageEventType type) + MessageEventType type) { return d->sendEvent<RoomMessageEvent>(plainText, type, new EventContent::TextContent(html, QStringLiteral("text/html"))); @@ -1530,7 +1523,7 @@ QString Room::postHtmlMessage(const QString& plainText, const QString& html, QString Room::postHtmlText(const QString& plainText, const QString& html) { - return postHtmlMessage(plainText, html, MessageEventType::Text); + return postHtmlMessage(plainText, html); } QString Room::postFile(const QString& plainText, const QUrl& localPath, @@ -1614,6 +1607,11 @@ void Room::setCanonicalAlias(const QString& newAlias) d->requestSetState(RoomCanonicalAliasEvent(newAlias)); } +void Room::setAliases(const QStringList& aliases) +{ + d->requestSetState(RoomAliasesEvent(aliases)); +} + void Room::setTopic(const QString& newTopic) { d->requestSetState(RoomTopicEvent(newTopic)); @@ -1881,22 +1879,29 @@ RoomEventPtr makeRedacted(const RoomEvent& target, const RedactionEvent& redaction) { auto originalJson = target.originalJsonObject(); - static const QStringList keepKeys = - { EventIdKey, TypeKey, QStringLiteral("room_id"), - QStringLiteral("sender"), QStringLiteral("state_key"), - QStringLiteral("prev_content"), ContentKey, - QStringLiteral("origin_server_ts") }; + static const QStringList keepKeys { + EventIdKey, TypeKey, QStringLiteral("room_id"), + QStringLiteral("sender"), QStringLiteral("state_key"), + QStringLiteral("prev_content"), ContentKey, + QStringLiteral("hashes"), QStringLiteral("signatures"), + QStringLiteral("depth"), QStringLiteral("prev_events"), + QStringLiteral("prev_state"), QStringLiteral("auth_events"), + QStringLiteral("origin"), QStringLiteral("origin_server_ts"), + QStringLiteral("membership") + }; std::vector<std::pair<Event::Type, QStringList>> keepContentKeysMap { { RoomMemberEvent::typeId(), { QStringLiteral("membership") } } - , { RoomCreateEvent::typeId(), { QStringLiteral("creator") } } + , { RoomCreateEvent::typeId(), { QStringLiteral("creator") } } // , { RoomJoinRules::typeId(), { QStringLiteral("join_rule") } } // , { RoomPowerLevels::typeId(), // { QStringLiteral("ban"), QStringLiteral("events"), // QStringLiteral("events_default"), QStringLiteral("kick"), // QStringLiteral("redact"), QStringLiteral("state_default"), // QStringLiteral("users"), QStringLiteral("users_default") } } - , { RoomAliasesEvent::typeId(), { QStringLiteral("alias") } } + , { RoomAliasesEvent::typeId(), { QStringLiteral("aliases") } } +// , { RoomHistoryVisibility::typeId(), +// { QStringLiteral("history_visibility") } } }; for (auto it = originalJson.begin(); it != originalJson.end();) { @@ -2050,15 +2055,21 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) it = nextPending + 1; auto* nextPendingEvt = nextPending->get(); - emit q->pendingEventAboutToMerge(nextPendingEvt, - int(nextPendingPair.second - unsyncedEvents.begin())); + const auto pendingEvtIdx = + int(nextPendingPair.second - unsyncedEvents.begin()); + emit q->pendingEventAboutToMerge(nextPendingEvt, pendingEvtIdx); qDebug(EVENTS) << "Merging pending event from transaction" << nextPendingEvt->transactionId() << "into" << nextPendingEvt->id(); auto transfer = fileTransfers.take(nextPendingEvt->transactionId()); if (transfer.status != FileTransferInfo::None) fileTransfers.insert(nextPendingEvt->id(), transfer); - unsyncedEvents.erase(nextPendingPair.second); + // After emitting pendingEventAboutToMerge() above we cannot rely + // on the previously obtained nextPendingPair.second staying valid + // because a signal handler may send another message, thereby altering + // unsyncedEvents (see #286). Fortunately, unsyncedEvents only grows at + // its back so we can rely on the index staying valid at least. + unsyncedEvents.erase(unsyncedEvents.begin() + pendingEvtIdx); if (auto insertedSize = moveEventsToTimeline({nextPending, it}, Newer)) { totalInserted += insertedSize; @@ -2148,8 +2159,12 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (!e.isStateEvent()) return Change::NoChange; - d->currentState[{e.matrixType(),e.stateKey()}] = - static_cast<const StateEventBase*>(&e); + const auto* oldStateEvent = std::exchange( + d->currentState[{e.matrixType(),e.stateKey()}], + static_cast<const StateEventBase*>(&e)); + Q_ASSERT(!oldStateEvent || + (oldStateEvent->matrixType() == e.matrixType() && + oldStateEvent->stateKey() == e.stateKey())); if (!is<RoomMemberEvent>(e)) qCDebug(EVENTS) << "Room state event:" << e; @@ -2157,7 +2172,11 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) , [] (const RoomNameEvent&) { return NameChange; } - , [] (const RoomAliasesEvent&) { + , [this,oldStateEvent] (const RoomAliasesEvent& ae) { + const auto previousAliases = oldStateEvent + ? static_cast<const RoomAliasesEvent*>(oldStateEvent)->aliases() + : QStringList(); + connection()->updateRoomAliases(id(), previousAliases, ae.aliases()); return OtherChange; } , [this] (const RoomCanonicalAliasEvent& evt) { |