aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/qmc-example.cpp65
-rw-r--r--lib/room.cpp32
2 files changed, 62 insertions, 35 deletions
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp
index f4067009..10ea9d35 100644
--- a/examples/qmc-example.cpp
+++ b/examples/qmc-example.cpp
@@ -375,42 +375,63 @@ void QMCTest::checkFileSendingOutcome(const QString& txnId,
void QMCTest::setTopic()
{
static const char* const stateTestName = "State setting test";
- static const char* const fakeStateTestName =
- "Fake state event immunity test";
running.push_back(stateTestName);
- running.push_back(fakeStateTestName);
- auto initialTopic = targetRoom->topic();
- const auto newTopic = c->generateTxnId();
+ const auto newTopic = c->generateTxnId(); // Just a way to get a unique id
targetRoom->setTopic(newTopic); // Sets the state by proper means
const auto fakeTopic = c->generateTxnId();
- targetRoom->postJson(RoomTopicEvent::matrixTypeId(), // Fake state event
- RoomTopicEvent(fakeTopic).contentJson());
+ const auto fakeTxnId =
+ targetRoom->postJson(RoomTopicEvent::matrixTypeId(), // Fake state event
+ RoomTopicEvent(fakeTopic).contentJson());
connectUntil(targetRoom, &Room::topicChanged, this,
- [this, newTopic, fakeTopic, initialTopic] {
+ [this, newTopic] {
if (targetRoom->topic() == newTopic) {
QMC_CHECK(stateTestName, true);
- // Don't reset the topic yet if the negative test still
- // runs
- if (!running.contains(fakeStateTestName))
- targetRoom->setTopic(initialTopic);
-
return true;
}
return false;
});
- connectUntil(targetRoom, &Room::pendingEventAboutToMerge, this,
- [this, fakeTopic, initialTopic](const RoomEvent* e, int) {
- if (e->contentJson().value("topic").toString() != fakeTopic)
- return false; // Wait on for the right event
+ // Older Synapses allowed sending fake state events through, although
+ // did not process them; // https://github.com/matrix-org/synapse/pull/5805
+ // changed that and now Synapse 400's in response to fake state events.
+ // The following two-step approach handles both cases, assuming that
+ // Room::pendingEventChanged() with EventStatus::ReachedServer is guaranteed
+ // to be emitted before Room::pendingEventAboutToMerge.
+ connectUntil(
+ targetRoom, &Room::pendingEventChanged, this,
+ [this, fakeTopic, fakeTxnId](int pendingIdx) {
+ const auto& pendingEvents = targetRoom->pendingEvents();
+ Q_ASSERT(pendingIdx >= 0 && pendingIdx < int(pendingEvents.size()));
+ const auto& evt = pendingEvents[pendingIdx];
+ if (evt->transactionId() != fakeTxnId)
+ return false;
- QMC_CHECK(fakeStateTestName, !e->isStateEvent());
- if (!running.contains(fakeStateTestName))
- targetRoom->setTopic(initialTopic);
- return true;
- });
+ if (evt.deliveryStatus() == EventStatus::SendingFailed) {
+ QMC_CHECK("Fake state event immunity test", true);
+ return true;
+ }
+ if (evt.deliveryStatus() != EventStatus::ReachedServer)
+ return false;
+
+ // All before was just a preparation, this is where the test starts.
+ // (If Synapse rejected the event the library immunity can't be
+ // tested.)
+ static const char* const fakeStateTestName =
+ "Fake state event immunity test";
+ running.push_back(fakeStateTestName);
+ connectUntil(
+ targetRoom, &Room::pendingEventAboutToMerge, this,
+ [this, fakeTopic](const RoomEvent* e, int) {
+ if (e->contentJson().value("topic").toString() != fakeTopic)
+ return false; // Wait on for the right event
+
+ QMC_CHECK(fakeStateTestName, !e->isStateEvent());
+ return true;
+ });
+ return true;
+ });
}
void QMCTest::addAndRemoveTag()
diff --git a/lib/room.cpp b/lib/room.cpp
index b32d3492..52f86616 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -1469,8 +1469,10 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent)
return;
}
- it->setReachedServer(call->eventId());
- emit q->pendingEventChanged(int(it - unsyncedEvents.begin()));
+ if (it->deliveryStatus() != EventStatus::ReachedServer) {
+ it->setReachedServer(call->eventId());
+ emit q->pendingEventChanged(int(it - unsyncedEvents.begin()));
+ }
});
} else
onEventSendingFailure(txnId);
@@ -1520,6 +1522,7 @@ QString Room::retryMessage(const QString& txnId)
" events are likely to be in the timeline after retry";
}
it->resetStatus();
+ emit pendingEventChanged(int(it - d->unsyncedEvents.begin()));
return d->doSendEvent(it->event());
}
@@ -2170,10 +2173,11 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
auto nextPendingPair = findFirstOf(it, events.end(),
unsyncedEvents.begin(),
unsyncedEvents.end(), isEchoEvent);
- auto nextPending = nextPendingPair.first;
+ const auto& remoteEcho = nextPendingPair.first;
+ const auto& localEcho = nextPendingPair.second;
- if (it != nextPending) {
- RoomEventsRange eventsSpan { it, nextPending };
+ if (it != remoteEcho) {
+ RoomEventsRange eventsSpan { it, remoteEcho };
emit q->aboutToAddNewMessages(eventsSpan);
auto insertedSize = moveEventsToTimeline(eventsSpan, Newer);
totalInserted += insertedSize;
@@ -2182,13 +2186,16 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
emit q->addedMessages(firstInserted->index(),
timeline.back().index());
}
- if (nextPending == events.end())
+ if (remoteEcho == events.end())
break;
- it = nextPending + 1;
- auto* nextPendingEvt = nextPending->get();
- const auto pendingEvtIdx =
- int(nextPendingPair.second - unsyncedEvents.begin());
+ it = remoteEcho + 1;
+ auto* nextPendingEvt = remoteEcho->get();
+ const auto pendingEvtIdx = int(localEcho - unsyncedEvents.begin());
+ if (localEcho->deliveryStatus() != EventStatus::ReachedServer) {
+ localEcho->setReachedServer(nextPendingEvt->id());
+ emit q->pendingEventChanged(pendingEvtIdx);
+ }
emit q->pendingEventAboutToMerge(nextPendingEvt, pendingEvtIdx);
qDebug(EVENTS) << "Merging pending event from transaction"
<< nextPendingEvt->transactionId() << "into"
@@ -2197,13 +2204,12 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
if (transfer.status != FileTransferInfo::None)
fileTransfers.insert(nextPendingEvt->id(), transfer);
// After emitting pendingEventAboutToMerge() above we cannot rely
- // on the previously obtained nextPendingPair.second staying valid
+ // on the previously obtained localEcho 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)) {
+ if (auto insertedSize = moveEventsToTimeline({ remoteEcho, it }, Newer)) {
totalInserted += insertedSize;
q->onAddNewTimelineEvents(timeline.cend() - insertedSize);
}