aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--README.md1
-rw-r--r--examples/qmc-example.cpp193
-rw-r--r--lib/connection.h21
-rw-r--r--lib/events/stateevent.cpp10
-rw-r--r--lib/qt_connection_util.h107
-rw-r--r--lib/util.h33
-rw-r--r--libqmatrixclient.pri1
8 files changed, 246 insertions, 126 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c48a7ba9..f0f8ac5a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.1)
project(qmatrixclient CXX)
+option(QMATRIXCLIENT_INSTALL_EXAMPLE "install qmc-example application" ON)
+
include(CheckCXXCompilerFlag)
if (NOT WIN32)
include(GNUInstallDirs)
@@ -200,7 +202,9 @@ if (WIN32)
install(FILES mime/packages/freedesktop.org.xml DESTINATION mime/packages)
endif (WIN32)
-install(TARGETS qmc-example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+if (QMATRIXCLIENT_INSTALL_EXAMPLE)
+ install(TARGETS qmc-example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif (QMATRIXCLIENT_INSTALL_EXAMPLE)
if (UNIX AND NOT APPLE)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/QMatrixClient.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
diff --git a/README.md b/README.md
index 58be072e..a0332bef 100644
--- a/README.md
+++ b/README.md
@@ -63,6 +63,7 @@ You can install the library with CMake:
cmake --build . --target install
```
This will also install cmake package config files; once this is done, you can use `examples/CMakeLists.txt` to compile the example with the _installed_ library. This file is a good starting point for your own CMake-based project using libQMatrixClient.
+Installation of `qmc-example` application can be skipped by setting `QMATRIXCLIENT_INSTALL_EXAMPLE` to `OFF`.
#### qmake-based
The library provides a .pri file with an intention to be included from a bigger project's .pro file. As a starting point you can use `qmc-example.pro` that will build a minimal example of library usage for you. In the root directory of the project sources:
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp
index 8fbf4824..421ead27 100644
--- a/examples/qmc-example.cpp
+++ b/examples/qmc-example.cpp
@@ -5,6 +5,7 @@
#include "csapi/room_send.h"
#include "csapi/joining.h"
#include "csapi/leaving.h"
+#include "events/simplestateevents.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QStringBuilder>
@@ -34,14 +35,14 @@ class QMCTest : public QObject
void sendFile();
void checkFileSendingOutcome(const QString& txnId,
const QString& fileName);
+ void setTopic();
void addAndRemoveTag();
void sendAndRedact();
- void checkRedactionOutcome(const QString& evtIdToRedact,
- const QMetaObject::Connection& sc);
+ bool checkRedactionOutcome(const QString& evtIdToRedact);
void markDirectChat();
void checkDirectChatOutcome(
const Connection::DirectChatsMap& added);
- void leave();
+ void conclude();
void finalize();
private:
@@ -95,7 +96,7 @@ QMCTest::QMCTest(Connection* conn, QString testRoomName, QString source)
connect(c.data(), &Connection::connected, this, &QMCTest::setupAndRun);
connect(c.data(), &Connection::loadedRoomState, this, &QMCTest::onNewRoom);
// Big countdown watchdog
- QTimer::singleShot(180000, this, &QMCTest::leave);
+ QTimer::singleShot(180000, this, &QMCTest::conclude);
}
void QMCTest::setupAndRun()
@@ -110,7 +111,7 @@ void QMCTest::setupAndRun()
running.push_back("Join room");
auto joinJob = c->joinRoom(targetRoomName);
connect(joinJob, &BaseJob::failure, this,
- [this] { QMC_CHECK("Join room", false); finalize(); });
+ [this] { QMC_CHECK("Join room", false); conclude(); });
// Connection::joinRoom() creates a Room object upon JoinRoomJob::success
// but this object is empty until the first sync is done.
connect(joinJob, &BaseJob::success, this, [this,joinJob] {
@@ -149,26 +150,12 @@ void QMCTest::run()
c->sync();
connectSingleShot(c.data(), &Connection::syncDone, this, &QMCTest::doTests);
connect(c.data(), &Connection::syncDone, c.data(), [this] {
- cout << "Sync complete, "
- << running.size() << " tests in the air" << endl;
+ cout << "Sync complete, " << running.size() << " test(s) in the air: "
+ << running.join(", ").toStdString() << endl;
if (!running.isEmpty())
c->sync(10000);
- else if (targetRoom)
- {
- // TODO: Waiting for proper futures to come so that it could be:
-// targetRoom->postPlainText(origin % ": All tests finished")
-// .then(this, &QMCTest::leave); // Qt-style
-// .then([this] { leave(); }); // STL-style
- auto txnId =
- targetRoom->postPlainText(origin % ": All tests finished");
- connect(targetRoom, &Room::messageSent, this,
- [this,txnId] (QString serverTxnId) {
- if (txnId == serverTxnId)
- leave();
- });
- }
else
- finalize();
+ conclude();
});
}
@@ -183,6 +170,7 @@ void QMCTest::doTests()
sendMessage();
sendFile();
+ setTopic();
addAndRemoveTag();
sendAndRedact();
markDirectChat();
@@ -230,21 +218,19 @@ void QMCTest::sendMessage()
return;
}
- QMetaObject::Connection sc;
- sc = connect(targetRoom, &Room::pendingEventAboutToMerge, this,
- [this,sc,txnId] (const RoomEvent* evt, int pendingIdx) {
+ connectUntil(targetRoom, &Room::pendingEventAboutToMerge, this,
+ [this,txnId] (const RoomEvent* evt, int pendingIdx) {
const auto& pendingEvents = targetRoom->pendingEvents();
Q_ASSERT(pendingIdx >= 0 && pendingIdx < int(pendingEvents.size()));
if (evt->transactionId() != txnId)
- return;
-
- disconnect(sc);
+ return false;
QMC_CHECK("Message sending",
is<RoomMessageEvent>(*evt) && !evt->id().isEmpty() &&
pendingEvents[size_t(pendingIdx)]->transactionId()
== evt->transactionId());
+ return true;
});
}
@@ -275,30 +261,26 @@ void QMCTest::sendFile()
return;
}
- QMetaObject::Connection scCompleted, scFailed;
- scCompleted = connect(targetRoom, &Room::fileTransferCompleted, this,
- [this,txnId,tf,tfName,scCompleted,scFailed] (const QString& id) {
+ // FIXME: Clean away connections (connectUntil doesn't help here).
+ connect(targetRoom, &Room::fileTransferCompleted, this,
+ [this,txnId,tf,tfName] (const QString& id) {
auto fti = targetRoom->fileTransferInfo(id);
Q_ASSERT(fti.status == FileTransferInfo::Completed);
if (id != txnId)
return;
- disconnect(scCompleted);
- disconnect(scFailed);
delete tf;
checkFileSendingOutcome(txnId, tfName);
});
- scFailed = connect(targetRoom, &Room::fileTransferFailed, this,
- [this,txnId,tf,scCompleted,scFailed]
+ connect(targetRoom, &Room::fileTransferFailed, this,
+ [this,txnId,tf]
(const QString& id, const QString& error) {
if (id != txnId)
return;
targetRoom->postPlainText(origin % ": File upload failed: " % error);
- disconnect(scCompleted);
- disconnect(scFailed);
delete tf;
QMC_CHECK("File sending", false);
@@ -325,18 +307,16 @@ void QMCTest::checkFileSendingOutcome(const QString& txnId,
return;
}
- QMetaObject::Connection sc;
- sc = connect(targetRoom, &Room::pendingEventAboutToMerge, this,
- [this,sc,txnId,fileName] (const RoomEvent* evt, int pendingIdx) {
+ connectUntil(targetRoom, &Room::pendingEventAboutToMerge, this,
+ [this,txnId,fileName] (const RoomEvent* evt, int pendingIdx) {
const auto& pendingEvents = targetRoom->pendingEvents();
Q_ASSERT(pendingIdx >= 0 && pendingIdx < int(pendingEvents.size()));
if (evt->transactionId() != txnId)
- return;
+ return false;
- cout << "Event " << txnId.toStdString()
+ cout << "File event " << txnId.toStdString()
<< " arrived in the timeline" << endl;
- disconnect(sc);
visit(*evt,
[&] (const RoomMessageEvent& e) {
QMC_CHECK("File sending",
@@ -349,6 +329,47 @@ void QMCTest::checkFileSendingOutcome(const QString& txnId,
[this] (const RoomEvent&) {
QMC_CHECK("File sending", false);
});
+ return true;
+ });
+}
+
+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();
+ targetRoom->setTopic(newTopic); // Sets the state by proper means
+ const auto fakeTopic = c->generateTxnId();
+ targetRoom->postJson(RoomTopicEvent::matrixTypeId(), // Fake state event
+ RoomTopicEvent(fakeTopic).contentJson());
+
+ connectUntil(targetRoom, &Room::topicChanged, this,
+ [this,newTopic,fakeTopic,initialTopic] {
+ 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
+
+ QMC_CHECK(fakeStateTestName, !e->isStateEvent());
+ if (!running.contains(fakeStateTestName))
+ targetRoom->setTopic(initialTopic);
+ return true;
});
}
@@ -370,7 +391,7 @@ void QMCTest::addAndRemoveTag()
cout << "Test tag set, removing it now" << endl;
targetRoom->removeTag(TestTag);
QMC_CHECK("Tagging test", !targetRoom->tags().contains(TestTag));
- QObject::disconnect(targetRoom, &Room::tagsChanged, nullptr, nullptr);
+ disconnect(targetRoom, &Room::tagsChanged, nullptr, nullptr);
}
});
cout << "Adding a tag" << endl;
@@ -394,41 +415,40 @@ void QMCTest::sendAndRedact()
cout << "Redacting the message" << endl;
targetRoom->redactEvent(evtId, origin);
- QMetaObject::Connection sc;
- sc = connect(targetRoom, &Room::addedMessages, this,
- [this,sc,evtId] { checkRedactionOutcome(evtId, sc); });
+
+ connectUntil(targetRoom, &Room::addedMessages, this,
+ [this,evtId] { return checkRedactionOutcome(evtId); });
});
}
-void QMCTest::checkRedactionOutcome(const QString& evtIdToRedact,
- const QMetaObject::Connection& sc)
+bool QMCTest::checkRedactionOutcome(const QString& evtIdToRedact)
{
// There are two possible (correct) outcomes: either the event comes already
// redacted at the next sync, or the nearest sync completes with
// the unredacted event but the next one brings redaction.
auto it = targetRoom->findInTimeline(evtIdToRedact);
if (it == targetRoom->timelineEdge())
- return; // Waiting for the next sync
+ return false; // Waiting for the next sync
if ((*it)->isRedacted())
{
cout << "The sync brought already redacted message" << endl;
QMC_CHECK("Redaction", true);
- disconnect(sc);
- return;
- }
- cout << "Message came non-redacted with the sync, waiting for redaction"
- << endl;
- connect(targetRoom, &Room::replacedEvent, this,
- [this,evtIdToRedact]
- (const RoomEvent* newEvent, const RoomEvent* oldEvent) {
- if (oldEvent->id() == evtIdToRedact)
- {
+ } else {
+ cout << "Message came non-redacted with the sync, waiting for redaction"
+ << endl;
+ connectUntil(targetRoom, &Room::replacedEvent, this,
+ [this,evtIdToRedact]
+ (const RoomEvent* newEvent, const RoomEvent* oldEvent) {
+ if (oldEvent->id() != evtIdToRedact)
+ return false;
+
QMC_CHECK("Redaction", newEvent->isRedacted() &&
newEvent->redactionReason() == origin);
- disconnect(targetRoom, &Room::replacedEvent, nullptr, nullptr);
- }
- });
+ return true;
+ });
+ }
+ return true;
}
void QMCTest::markDirectChat()
@@ -469,13 +489,47 @@ void QMCTest::checkDirectChatOutcome(const Connection::DirectChatsMap& added)
QMC_CHECK("Direct chat test", !c->isDirectChat(targetRoom->id()));
}
-void QMCTest::leave()
+void QMCTest::conclude()
{
+ auto succeededRec = QString::number(succeeded.size()) + " tests succeeded";
+ if (!failed.isEmpty() || !running.isEmpty())
+ succeededRec += " of " %
+ QString::number(succeeded.size() + failed.size() + running.size()) %
+ " total";
+ QString plainReport = origin % ": Testing complete, " % succeededRec;
+ QString color = failed.isEmpty() && running.isEmpty() ? "00AA00" : "AA0000";
+ QString htmlReport = origin % ": <strong><font data-mx-color='#" % color %
+ "' color='#" % color % "'>Testing complete</font></strong>, " %
+ succeededRec;
+ if (!failed.isEmpty())
+ {
+ plainReport += "\nFAILED: " % failed.join(", ");
+ htmlReport += "<br><strong>Failed:</strong> " % failed.join(", ");
+ }
+ if (!running.isEmpty())
+ {
+ plainReport += "\nDID NOT FINISH: " % running.join(", ");
+ htmlReport +=
+ "<br><strong>Did not finish:</strong> " % running.join(", ");
+ }
+ cout << plainReport.toStdString() << endl;
+
if (targetRoom)
{
- cout << "Leaving the room" << endl;
- connect(targetRoom->leaveRoom(), &BaseJob::finished,
- this, &QMCTest::finalize);
+ // TODO: Waiting for proper futures to come so that it could be:
+// targetRoom->postHtmlText(...)
+// .then(this, &QMCTest::finalize); // Qt-style or
+// .then([this] { finalize(); }); // STL-style
+ auto txnId = targetRoom->postHtmlText(plainReport, htmlReport);
+ connect(targetRoom, &Room::messageSent, this,
+ [this,txnId] (QString serverTxnId) {
+ if (txnId != serverTxnId)
+ return;
+
+ cout << "Leaving the room" << endl;
+ connect(targetRoom->leaveRoom(), &BaseJob::finished,
+ this, &QMCTest::finalize);
+ });
}
else
finalize();
@@ -487,11 +541,6 @@ void QMCTest::finalize()
c->logout();
connect(c.data(), &Connection::loggedOut, qApp,
[this] {
- if (!failed.isEmpty())
- cout << "FAILED: " << failed.join(", ").toStdString() << endl;
- if (!running.isEmpty())
- cout << "DID NOT FINISH: "
- << running.join(", ").toStdString() << endl;
QCoreApplication::processEvents();
QCoreApplication::exit(failed.size() + running.size());
});
diff --git a/lib/connection.h b/lib/connection.h
index cba57e3d..9e4121f4 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -21,6 +21,7 @@
#include "csapi/create_room.h"
#include "joinstate.h"
#include "events/accountdataevents.h"
+#include "qt_connection_util.h"
#include <QtCore/QObject>
#include <QtCore/QUrl>
@@ -50,26 +51,6 @@ namespace QMatrixClient
class SendMessageJob;
class LeaveRoomJob;
- /** Create a single-shot connection that triggers on the signal and
- * then self-disconnects
- *
- * Only supports DirectConnection type.
- */
- template <typename SenderT1, typename SignalT,
- typename ReceiverT2, typename SlotT>
- inline auto connectSingleShot(SenderT1* sender, SignalT signal,
- ReceiverT2* receiver, SlotT slot)
- {
- QMetaObject::Connection connection;
- connection = QObject::connect(sender, signal, receiver, slot,
- Qt::DirectConnection);
- Q_ASSERT(connection);
- QObject::connect(sender, signal, receiver,
- [connection] { QObject::disconnect(connection); },
- Qt::DirectConnection);
- return connection;
- }
-
class Connection;
using room_factory_t = std::function<Room*(Connection*, const QString&,
diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp
index c4151676..e96614d2 100644
--- a/lib/events/stateevent.cpp
+++ b/lib/events/stateevent.cpp
@@ -25,13 +25,15 @@ using namespace QMatrixClient;
// but the event type is unknown.
[[gnu::unused]] static auto stateEventTypeInitialised =
RoomEvent::factory_t::addMethod(
- [] (const QJsonObject& json, const QString& matrixType)
+ [] (const QJsonObject& json, const QString& matrixType) -> StateEventPtr
{
+ if (!json.contains("state_key"))
+ return nullptr;
+
if (auto e = StateEventBase::factory_t::make(json, matrixType))
return e;
- return json.contains("state_key")
- ? makeEvent<StateEventBase>(unknownEventTypeId(), json)
- : nullptr;
+
+ return makeEvent<StateEventBase>(unknownEventTypeId(), json);
});
bool StateEventBase::repeatsState() const
diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h
new file mode 100644
index 00000000..c2bde8df
--- /dev/null
+++ b/lib/qt_connection_util.h
@@ -0,0 +1,107 @@
+/******************************************************************************
+ * Copyright (C) 2019 Kitsune Ral <kitsune-ral@users.sf.net>
+ *
+ * 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
+ */
+
+#pragma once
+
+#include "util.h"
+
+#include <QtCore/QPointer>
+
+namespace QMatrixClient {
+ namespace _impl {
+ template <typename SenderT, typename SignalT,
+ typename ContextT, typename... ArgTs>
+ inline QMetaObject::Connection connectUntil(
+ SenderT* sender, SignalT signal, ContextT* context,
+ std::function<bool(ArgTs...)> slot, Qt::ConnectionType connType)
+ {
+ // See https://bugreports.qt.io/browse/QTBUG-60339
+#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
+ auto pc = std::make_shared<QMetaObject::Connection>();
+#else
+ auto pc = std::make_unique<QMetaObject::Connection>();
+#endif
+ auto& c = *pc; // Resolve a reference before pc is moved to lambda
+ c = QObject::connect(sender, signal, context,
+ [pc=std::move(pc),slot] (ArgTs... args) {
+ Q_ASSERT(*pc); // If it's been triggered, it should exist
+ if (slot(std::forward<ArgTs>(args)...))
+ QObject::disconnect(*pc);
+ }, connType);
+ return c;
+ }
+ }
+
+ template <typename SenderT, typename SignalT,
+ typename ContextT, typename FunctorT>
+ inline auto connectUntil(SenderT* sender, SignalT signal, ContextT* context,
+ const FunctorT& slot,
+ Qt::ConnectionType connType = Qt::AutoConnection)
+ {
+ return _impl::connectUntil(sender, signal, context,
+ typename function_traits<FunctorT>::function_type(slot),
+ connType);
+ }
+
+ /** Create a single-shot connection that triggers on the signal and
+ * then self-disconnects
+ *
+ * Only supports DirectConnection type.
+ */
+ template <typename SenderT, typename SignalT,
+ typename ReceiverT, typename SlotT>
+ inline auto connectSingleShot(SenderT* sender, SignalT signal,
+ ReceiverT* receiver, SlotT slot)
+ {
+ QMetaObject::Connection connection;
+ connection = QObject::connect(sender, signal, receiver, slot,
+ Qt::DirectConnection);
+ Q_ASSERT(connection);
+ QObject::connect(sender, signal, receiver,
+ [connection] { QObject::disconnect(connection); },
+ Qt::DirectConnection);
+ return connection;
+ }
+
+ /** A guard pointer that disconnects an interested object upon destruction
+ * It's almost QPointer<> except that you have to initialise it with one
+ * more additional parameter - a pointer to a QObject that will be
+ * disconnected from signals of the underlying pointer upon the guard's
+ * destruction.
+ */
+ template <typename T>
+ class ConnectionsGuard : public QPointer<T>
+ {
+ public:
+ ConnectionsGuard(T* publisher, QObject* subscriber)
+ : QPointer<T>(publisher), subscriber(subscriber)
+ { }
+ ~ConnectionsGuard()
+ {
+ if (*this)
+ (*this)->disconnect(subscriber);
+ }
+ ConnectionsGuard(ConnectionsGuard&&) = default;
+ ConnectionsGuard& operator=(ConnectionsGuard&&) = default;
+ Q_DISABLE_COPY(ConnectionsGuard)
+ using QPointer<T>::operator=;
+
+ private:
+ QObject* subscriber;
+ };
+}
diff --git a/lib/util.h b/lib/util.h
index 77c2bfdb..ade6e8c2 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -18,8 +18,9 @@
#pragma once
-#include <QtCore/QPointer>
-#if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
+#include <QtCore/QLatin1String>
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
#include <QtCore/QMetaEnum>
#include <QtCore/QDebug>
#endif
@@ -185,6 +186,7 @@ namespace QMatrixClient
static constexpr auto is_callable = true;
using return_type = ReturnT;
using arg_types = std::tuple<ArgTs...>;
+ using function_type = std::function<ReturnT(ArgTs...)>;
static constexpr auto arg_number = std::tuple_size<arg_types>::value;
};
@@ -284,33 +286,6 @@ namespace QMatrixClient
return std::make_pair(last, sLast);
}
- /** A guard pointer that disconnects an interested object upon destruction
- * It's almost QPointer<> except that you have to initialise it with one
- * more additional parameter - a pointer to a QObject that will be
- * disconnected from signals of the underlying pointer upon the guard's
- * destruction.
- */
- template <typename T>
- class ConnectionsGuard : public QPointer<T>
- {
- public:
- ConnectionsGuard(T* publisher, QObject* subscriber)
- : QPointer<T>(publisher), subscriber(subscriber)
- { }
- ~ConnectionsGuard()
- {
- if (*this)
- (*this)->disconnect(subscriber);
- }
- ConnectionsGuard(ConnectionsGuard&&) = default;
- ConnectionsGuard& operator=(ConnectionsGuard&&) = default;
- Q_DISABLE_COPY(ConnectionsGuard)
- using QPointer<T>::operator=;
-
- private:
- QObject* subscriber;
- };
-
/** Pretty-prints plain text into HTML
* This includes HTML escaping of <,>,",& and URLs linkification.
*/
diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri
index eefaec67..f523f3a2 100644
--- a/libqmatrixclient.pri
+++ b/libqmatrixclient.pri
@@ -19,6 +19,7 @@ HEADERS += \
$$SRCPATH/avatar.h \
$$SRCPATH/syncdata.h \
$$SRCPATH/util.h \
+ $$SRCPATH/qt_connection_util.h \
$$SRCPATH/events/event.h \
$$SRCPATH/events/roomevent.h \
$$SRCPATH/events/stateevent.h \