aboutsummaryrefslogtreecommitdiff
path: root/lib/events
diff options
context:
space:
mode:
authorn-peugnet <n.peugnet@free.fr>2022-10-06 19:27:24 +0200
committern-peugnet <n.peugnet@free.fr>2022-10-06 19:27:24 +0200
commit08632625e1a04257b5c7d4a9db2246ac07436748 (patch)
tree9ddadf219a7e352ddd3549ad1683282c944adfb6 /lib/events
parente9c2e2a26d3711e755aa5eb8a8478917c71d612b (diff)
parentd911b207f49e936b3e992200796110f0749ed150 (diff)
downloadlibquotient-08632625e1a04257b5c7d4a9db2246ac07436748.tar.gz
libquotient-08632625e1a04257b5c7d4a9db2246ac07436748.zip
Update upstream source from tag 'upstream/0.7.0'
Update to upstream version '0.7.0' with Debian dir 30dcb77a77433e4a54eab77c0b82ae925dead2d8
Diffstat (limited to 'lib/events')
-rw-r--r--lib/events/accountdataevents.h91
-rw-r--r--lib/events/callanswerevent.cpp71
-rw-r--r--lib/events/callanswerevent.h45
-rw-r--r--lib/events/callcandidatesevent.cpp41
-rw-r--r--lib/events/callcandidatesevent.h45
-rw-r--r--lib/events/callevents.cpp82
-rw-r--r--lib/events/callevents.h99
-rw-r--r--lib/events/callhangupevent.cpp52
-rw-r--r--lib/events/callhangupevent.h33
-rw-r--r--lib/events/callinviteevent.cpp63
-rw-r--r--lib/events/callinviteevent.h44
-rw-r--r--lib/events/directchatevent.cpp21
-rw-r--r--lib/events/directchatevent.h26
-rw-r--r--lib/events/encryptedevent.cpp73
-rw-r--r--lib/events/encryptedevent.h50
-rw-r--r--lib/events/encryptionevent.cpp74
-rw-r--r--lib/events/encryptionevent.h74
-rw-r--r--lib/events/event.cpp81
-rw-r--r--lib/events/event.h740
-rw-r--r--lib/events/eventcontent.cpp133
-rw-r--r--lib/events/eventcontent.h506
-rw-r--r--lib/events/eventloader.h85
-rw-r--r--lib/events/eventrelation.cpp38
-rw-r--r--lib/events/eventrelation.h52
-rw-r--r--lib/events/filesourceinfo.cpp163
-rw-r--r--lib/events/filesourceinfo.h90
-rw-r--r--lib/events/keyverificationevent.h258
-rw-r--r--lib/events/reactionevent.cpp44
-rw-r--r--lib/events/reactionevent.h69
-rw-r--r--lib/events/receiptevent.cpp61
-rw-r--r--lib/events/receiptevent.h44
-rw-r--r--lib/events/redactionevent.cpp1
-rw-r--r--lib/events/redactionevent.h29
-rw-r--r--lib/events/roomavatarevent.h44
-rw-r--r--lib/events/roomcanonicalaliasevent.h88
-rw-r--r--lib/events/roomcreateevent.cpp41
-rw-r--r--lib/events/roomcreateevent.h31
-rw-r--r--lib/events/roomevent.cpp82
-rw-r--r--lib/events/roomevent.h106
-rw-r--r--lib/events/roomkeyevent.cpp9
-rw-r--r--lib/events/roomkeyevent.h30
-rw-r--r--lib/events/roommemberevent.cpp107
-rw-r--r--lib/events/roommemberevent.h115
-rw-r--r--lib/events/roommessageevent.cpp178
-rw-r--r--lib/events/roommessageevent.h112
-rw-r--r--lib/events/roompowerlevelsevent.cpp69
-rw-r--r--lib/events/roompowerlevelsevent.h35
-rw-r--r--lib/events/roomtombstoneevent.cpp23
-rw-r--r--lib/events/roomtombstoneevent.h29
-rw-r--r--lib/events/simplestateevents.h108
-rw-r--r--lib/events/single_key_value.h36
-rw-r--r--lib/events/stateevent.cpp62
-rw-r--r--lib/events/stateevent.h198
-rw-r--r--lib/events/stickerevent.h48
-rw-r--r--lib/events/typingevent.cpp31
-rw-r--r--lib/events/typingevent.h32
56 files changed, 2541 insertions, 2451 deletions
diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h
index a55016d9..324ce449 100644
--- a/lib/events/accountdataevents.h
+++ b/lib/events/accountdataevents.h
@@ -1,47 +1,26 @@
-/******************************************************************************
- * Copyright (C) 2018 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
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
-#include "converters.h"
#include "event.h"
-#include "eventcontent.h"
namespace Quotient {
-constexpr const char* FavouriteTag = "m.favourite";
-constexpr const char* LowPriorityTag = "m.lowpriority";
-constexpr const char* ServerNoticeTag = "m.server_notice";
+constexpr auto FavouriteTag [[maybe_unused]] = "m.favourite"_ls;
+constexpr auto LowPriorityTag [[maybe_unused]] = "m.lowpriority"_ls;
+constexpr auto ServerNoticeTag [[maybe_unused]] = "m.server_notice"_ls;
struct TagRecord {
- using order_type = Omittable<float>;
-
- order_type order;
-
- TagRecord(order_type order = none) : order(std::move(order)) {}
-
- bool operator<(const TagRecord& other) const
- {
- // Per The Spec, rooms with no order should be after those with order,
- // against optional<>::operator<() convention.
- return order && (!other.order || *order < *other.order);
- }
+ Omittable<float> order = none;
};
+inline bool operator<(TagRecord lhs, TagRecord rhs)
+{
+ // Per The Spec, rooms with no order should be after those with order,
+ // against std::optional<>::operator<() convention.
+ return lhs.order && (!rhs.order || *lhs.order < *rhs.order);
+}
+
template <>
struct JsonObjectConverter<TagRecord> {
static void fillFrom(const QJsonObject& jo, TagRecord& rec)
@@ -52,13 +31,13 @@ struct JsonObjectConverter<TagRecord> {
if (orderJv.isDouble())
rec.order = fromJson<float>(orderJv);
if (orderJv.isString()) {
- bool ok;
+ bool ok = false;
rec.order = orderJv.toString().toFloat(&ok);
if (!ok)
rec.order = none;
}
}
- static void dumpTo(QJsonObject& jo, const TagRecord& rec)
+ static void dumpTo(QJsonObject& jo, TagRecord rec)
{
addParam<IfNotEmpty>(jo, QStringLiteral("order"), rec.order);
}
@@ -66,27 +45,21 @@ struct JsonObjectConverter<TagRecord> {
using TagsMap = QHash<QString, TagRecord>;
-#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \
- class _Name : public Event { \
- public: \
- using content_type = _ContentType; \
- DEFINE_EVENT_TYPEID(_TypeId, _Name) \
- explicit _Name(QJsonObject obj) : Event(typeId(), std::move(obj)) {} \
- explicit _Name(_ContentType content) \
- : Event(typeId(), matrixTypeId(), \
- QJsonObject { { QStringLiteral(#_ContentKey), \
- toJson(std::move(content)) } }) \
- {} \
- auto _ContentKey() const \
- { \
- return content<content_type>(#_ContentKey##_ls); \
- } \
- }; \
- REGISTER_EVENT_TYPE(_Name) \
- // End of macro
-
-DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", TagsMap, tags)
-DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", QString, event_id)
-DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, "m.ignored_user_list", QSet<QString>,
- ignored_users)
+DEFINE_SIMPLE_EVENT(TagEvent, Event, "m.tag", TagsMap, tags, "tags")
+DEFINE_SIMPLE_EVENT(ReadMarkerEventImpl, Event, "m.fully_read", QString,
+ eventId, "event_id")
+class ReadMarkerEvent : public ReadMarkerEventImpl {
+public:
+ using ReadMarkerEventImpl::ReadMarkerEventImpl;
+ [[deprecated("Use ReadMarkerEvent::eventId() instead")]]
+ auto event_id() const { return eventId(); }
+};
+DEFINE_SIMPLE_EVENT(IgnoredUsersEventImpl, Event, "m.ignored_user_list",
+ QSet<QString>, ignoredUsers, "ignored_users")
+class IgnoredUsersEvent : public IgnoredUsersEventImpl {
+public:
+ using IgnoredUsersEventImpl::IgnoredUsersEventImpl;
+ [[deprecated("Use IgnoredUsersEvent::ignoredUsers() instead")]]
+ auto ignored_users() const { return ignoredUsers(); }
+};
} // namespace Quotient
diff --git a/lib/events/callanswerevent.cpp b/lib/events/callanswerevent.cpp
deleted file mode 100644
index d6622b30..00000000
--- a/lib/events/callanswerevent.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Marius Gripsgard <marius@ubports.com>
- *
- * 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
- */
-
-#include "callanswerevent.h"
-
-#include "event.h"
-#include "logging.h"
-
-#include <QtCore/QJsonDocument>
-
-/*
-m.call.answer
-{
- "age": 242352,
- "content": {
- "answer": {
- "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
- "type": "answer"
- },
- "call_id": "12345",
- "lifetime": 60000,
- "version": 0
- },
- "event_id": "$WLGTSEFSEF:localhost",
- "origin_server_ts": 1431961217939,
- "room_id": "!Cuyf34gef24t:localhost",
- "sender": "@example:localhost",
- "type": "m.call.answer"
-}
-*/
-
-using namespace Quotient;
-
-CallAnswerEvent::CallAnswerEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
-{
- qCDebug(EVENTS) << "Call Answer event";
-}
-
-CallAnswerEvent::CallAnswerEvent(const QString& callId, const int lifetime,
- const QString& sdp)
- : CallEventBase(
- typeId(), matrixTypeId(), callId, 0,
- { { QStringLiteral("lifetime"), lifetime },
- { QStringLiteral("answer"),
- QJsonObject { { QStringLiteral("type"), QStringLiteral("answer") },
- { QStringLiteral("sdp"), sdp } } } })
-{}
-
-CallAnswerEvent::CallAnswerEvent(const QString& callId, const QString& sdp)
- : CallEventBase(
- typeId(), matrixTypeId(), callId, 0,
- { { QStringLiteral("answer"),
- QJsonObject { { QStringLiteral("type"), QStringLiteral("answer") },
- { QStringLiteral("sdp"), sdp } } } })
-{}
diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h
deleted file mode 100644
index 2709882b..00000000
--- a/lib/events/callanswerevent.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Marius Gripsgard <marius@ubports.com>
- *
- * 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 "roomevent.h"
-
-namespace Quotient {
-class CallAnswerEvent : public CallEventBase {
-public:
- DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent)
-
- explicit CallAnswerEvent(const QJsonObject& obj);
-
- explicit CallAnswerEvent(const QString& callId, const int lifetime,
- const QString& sdp);
- explicit CallAnswerEvent(const QString& callId, const QString& sdp);
-
- int lifetime() const
- {
- return content<int>("lifetime"_ls);
- } // FIXME: Omittable<>?
- QString sdp() const
- {
- return contentJson()["answer"_ls].toObject().value("sdp"_ls).toString();
- }
-};
-
-REGISTER_EVENT_TYPE(CallAnswerEvent)
-} // namespace Quotient
diff --git a/lib/events/callcandidatesevent.cpp b/lib/events/callcandidatesevent.cpp
deleted file mode 100644
index 24f0dd46..00000000
--- a/lib/events/callcandidatesevent.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Marius Gripsgard <marius@ubports.com>
- *
- * 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
- */
-
-#include "callcandidatesevent.h"
-
-/*
-m.call.candidates
-{
- "age": 242352,
- "content": {
- "call_id": "12345",
- "candidates": [
- {
- "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156
-43670 typ host generation 0", "sdpMLineIndex": 0, "sdpMid": "audio"
- }
- ],
- "version": 0
- },
- "event_id": "$WLGTSEFSEF:localhost",
- "origin_server_ts": 1431961217939,
- "room_id": "!Cuyf34gef24t:localhost",
- "sender": "@example:localhost",
- "type": "m.call.candidates"
-}
-*/
diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h
deleted file mode 100644
index e224f048..00000000
--- a/lib/events/callcandidatesevent.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Marius Gripsgard <marius@ubports.com>
- *
- * 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 "roomevent.h"
-
-namespace Quotient {
-class CallCandidatesEvent : public CallEventBase {
-public:
- DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent)
-
- explicit CallCandidatesEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
- {}
-
- explicit CallCandidatesEvent(const QString& callId,
- const QJsonArray& candidates)
- : CallEventBase(typeId(), matrixTypeId(), callId, 0,
- { { QStringLiteral("candidates"), candidates } })
- {}
-
- QJsonArray candidates() const
- {
- return content<QJsonArray>("candidates"_ls);
- }
-};
-
-REGISTER_EVENT_TYPE(CallCandidatesEvent)
-} // namespace Quotient
diff --git a/lib/events/callevents.cpp b/lib/events/callevents.cpp
new file mode 100644
index 00000000..3873614d
--- /dev/null
+++ b/lib/events/callevents.cpp
@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: 2022 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "callevents.h"
+
+#include "logging.h"
+
+using namespace Quotient;
+
+QJsonObject CallEvent::basicJson(const QString& matrixType,
+ const QString& callId, int version,
+ QJsonObject contentJson)
+{
+ contentJson.insert(QStringLiteral("call_id"), callId);
+ contentJson.insert(QStringLiteral("version"), version);
+ return RoomEvent::basicJson(matrixType, contentJson);
+}
+
+CallEvent::CallEvent(const QJsonObject& json)
+ : RoomEvent(json)
+{
+ if (callId().isEmpty())
+ qCWarning(EVENTS) << id() << "is a call event with an empty call id";
+}
+
+/*
+m.call.invite
+{
+ "age": 242352,
+ "content": {
+ "call_id": "12345",
+ "lifetime": 60000,
+ "offer": {
+ "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
+ "type": "offer"
+ },
+ "version": 0
+ },
+ "event_id": "$WLGTSEFSEF:localhost",
+ "origin_server_ts": 1431961217939,
+ "room_id": "!Cuyf34gef24t:localhost",
+ "sender": "@example:localhost",
+ "type": "m.call.invite"
+}
+*/
+
+CallInviteEvent::CallInviteEvent(const QString& callId, int lifetime,
+ const QString& sdp)
+ : EventTemplate(
+ callId,
+ { { QStringLiteral("lifetime"), lifetime },
+ { QStringLiteral("offer"),
+ QJsonObject{ { QStringLiteral("type"), QStringLiteral("offer") },
+ { QStringLiteral("sdp"), sdp } } } })
+{}
+
+/*
+m.call.answer
+{
+ "age": 242352,
+ "content": {
+ "answer": {
+ "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
+ "type": "answer"
+ },
+ "call_id": "12345",
+ "version": 0
+ },
+ "event_id": "$WLGTSEFSEF:localhost",
+ "origin_server_ts": 1431961217939,
+ "room_id": "!Cuyf34gef24t:localhost",
+ "sender": "@example:localhost",
+ "type": "m.call.answer"
+}
+*/
+
+CallAnswerEvent::CallAnswerEvent(const QString& callId, const QString& sdp)
+ : EventTemplate(callId, { { QStringLiteral("answer"),
+ QJsonObject { { QStringLiteral("type"),
+ QStringLiteral("answer") },
+ { QStringLiteral("sdp"), sdp } } } })
+{}
diff --git a/lib/events/callevents.h b/lib/events/callevents.h
new file mode 100644
index 00000000..752e331d
--- /dev/null
+++ b/lib/events/callevents.h
@@ -0,0 +1,99 @@
+// SPDX-FileCopyrightText: 2022 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+
+#include "roomevent.h"
+
+namespace Quotient {
+
+class QUOTIENT_API CallEvent : public RoomEvent {
+public:
+ QUO_BASE_EVENT(CallEvent, "m.call.*"_ls, RoomEvent::BaseMetaType)
+ static bool matches(const QJsonObject&, const QString& mType)
+ {
+ return mType.startsWith("m.call.");
+ }
+
+ QUO_CONTENT_GETTER(QString, callId)
+ QUO_CONTENT_GETTER(int, version)
+
+protected:
+ explicit CallEvent(const QJsonObject& json);
+
+ static QJsonObject basicJson(const QString& matrixType,
+ const QString& callId, int version,
+ QJsonObject contentJson = {});
+};
+using CallEventBase
+ [[deprecated("CallEventBase is CallEvent now")]] = CallEvent;
+
+template <typename EventT>
+class EventTemplate<EventT, CallEvent> : public CallEvent {
+public:
+ using CallEvent::CallEvent;
+ explicit EventTemplate(const QString& callId,
+ const QJsonObject& contentJson = {})
+ : EventTemplate(basicJson(EventT::TypeId, callId, 0, contentJson))
+ {}
+};
+
+template <typename EventT, typename ContentT>
+class EventTemplate<EventT, CallEvent, ContentT>
+ : public EventTemplate<EventT, CallEvent> {
+public:
+ using EventTemplate<EventT, CallEvent>::EventTemplate;
+ template <typename... ContentParamTs>
+ explicit EventTemplate(const QString& callId,
+ ContentParamTs&&... contentParams)
+ : EventTemplate<EventT, CallEvent>(
+ callId,
+ toJson(ContentT{ std::forward<ContentParamTs>(contentParams)... }))
+ {}
+};
+
+class QUOTIENT_API CallInviteEvent
+ : public EventTemplate<CallInviteEvent, CallEvent> {
+public:
+ QUO_EVENT(CallInviteEvent, "m.call.invite")
+
+ using EventTemplate::EventTemplate;
+
+ explicit CallInviteEvent(const QString& callId, int lifetime,
+ const QString& sdp);
+
+ QUO_CONTENT_GETTER(int, lifetime)
+ QString sdp() const
+ {
+ return contentPart<QJsonObject>("offer"_ls).value("sdp"_ls).toString();
+ }
+};
+
+DEFINE_SIMPLE_EVENT(CallCandidatesEvent, CallEvent, "m.call.candidates",
+ QJsonArray, candidates, "candidates")
+
+class QUOTIENT_API CallAnswerEvent
+ : public EventTemplate<CallAnswerEvent, CallEvent> {
+public:
+ QUO_EVENT(CallAnswerEvent, "m.call.answer")
+
+ using EventTemplate::EventTemplate;
+
+ explicit CallAnswerEvent(const QString& callId, const QString& sdp);
+
+ QString sdp() const
+ {
+ return contentPart<QJsonObject>("answer"_ls).value("sdp"_ls).toString();
+ }
+};
+
+class QUOTIENT_API CallHangupEvent
+ : public EventTemplate<CallHangupEvent, CallEvent> {
+public:
+ QUO_EVENT(CallHangupEvent, "m.call.hangup")
+ using EventTemplate::EventTemplate;
+};
+
+} // namespace Quotient
+Q_DECLARE_METATYPE(Quotient::CallEvent*)
+Q_DECLARE_METATYPE(const Quotient::CallEvent*)
diff --git a/lib/events/callhangupevent.cpp b/lib/events/callhangupevent.cpp
deleted file mode 100644
index d41849c3..00000000
--- a/lib/events/callhangupevent.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Marius Gripsgard <marius@ubports.com>
- *
- * 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
- */
-
-#include "callhangupevent.h"
-
-#include "event.h"
-#include "logging.h"
-
-#include <QtCore/QJsonDocument>
-
-/*
-m.call.hangup
-{
- "age": 242352,
- "content": {
- "call_id": "12345",
- "version": 0
- },
- "event_id": "$WLGTSEFSEF:localhost",
- "origin_server_ts": 1431961217939,
- "room_id": "!Cuyf34gef24t:localhost",
- "sender": "@example:localhost",
- "type": "m.call.hangup"
-}
-*/
-
-using namespace Quotient;
-
-CallHangupEvent::CallHangupEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
-{
- qCDebug(EVENTS) << "Call Hangup event";
-}
-
-CallHangupEvent::CallHangupEvent(const QString& callId)
- : CallEventBase(typeId(), matrixTypeId(), callId, 0)
-{}
diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h
deleted file mode 100644
index 5d73fb62..00000000
--- a/lib/events/callhangupevent.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Marius Gripsgard <marius@ubports.com>
- *
- * 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 "roomevent.h"
-
-namespace Quotient {
-class CallHangupEvent : public CallEventBase {
-public:
- DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent)
-
- explicit CallHangupEvent(const QJsonObject& obj);
- explicit CallHangupEvent(const QString& callId);
-};
-
-REGISTER_EVENT_TYPE(CallHangupEvent)
-} // namespace Quotient
diff --git a/lib/events/callinviteevent.cpp b/lib/events/callinviteevent.cpp
deleted file mode 100644
index 54faac8d..00000000
--- a/lib/events/callinviteevent.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Marius Gripsgard <marius@ubports.com>
- *
- * 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
- */
-
-#include "callinviteevent.h"
-
-#include "event.h"
-#include "logging.h"
-
-#include <QtCore/QJsonDocument>
-
-/*
-m.call.invite
-{
- "age": 242352,
- "content": {
- "call_id": "12345",
- "lifetime": 60000,
- "offer": {
- "sdp": "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]",
- "type": "offer"
- },
- "version": 0
- },
- "event_id": "$WLGTSEFSEF:localhost",
- "origin_server_ts": 1431961217939,
- "room_id": "!Cuyf34gef24t:localhost",
- "sender": "@example:localhost",
- "type": "m.call.invite"
-}
-*/
-
-using namespace Quotient;
-
-CallInviteEvent::CallInviteEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
-{
- qCDebug(EVENTS) << "Call Invite event";
-}
-
-CallInviteEvent::CallInviteEvent(const QString& callId, const int lifetime,
- const QString& sdp)
- : CallEventBase(
- typeId(), matrixTypeId(), callId, lifetime,
- { { QStringLiteral("lifetime"), lifetime },
- { QStringLiteral("offer"),
- QJsonObject { { QStringLiteral("type"), QStringLiteral("offer") },
- { QStringLiteral("sdp"), sdp } } } })
-{}
diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h
deleted file mode 100644
index b067a492..00000000
--- a/lib/events/callinviteevent.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Marius Gripsgard <marius@ubports.com>
- *
- * 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 "roomevent.h"
-
-namespace Quotient {
-class CallInviteEvent : public CallEventBase {
-public:
- DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent)
-
- explicit CallInviteEvent(const QJsonObject& obj);
-
- explicit CallInviteEvent(const QString& callId, const int lifetime,
- const QString& sdp);
-
- int lifetime() const
- {
- return content<int>("lifetime"_ls);
- } // FIXME: Omittable<>?
- QString sdp() const
- {
- return contentJson()["offer"_ls].toObject().value("sdp"_ls).toString();
- }
-};
-
-REGISTER_EVENT_TYPE(CallInviteEvent)
-} // namespace Quotient
diff --git a/lib/events/directchatevent.cpp b/lib/events/directchatevent.cpp
index b4027e16..83bb1e32 100644
--- a/lib/events/directchatevent.cpp
+++ b/lib/events/directchatevent.cpp
@@ -1,25 +1,8 @@
-/******************************************************************************
- * Copyright (C) 2018 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
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "directchatevent.h"
-#include <QtCore/QJsonArray>
-
using namespace Quotient;
QMultiHash<QString, QString> DirectChatEvent::usersToDirectChats() const
diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h
index bb091c5c..0756d816 100644
--- a/lib/events/directchatevent.h
+++ b/lib/events/directchatevent.h
@@ -1,33 +1,17 @@
-/******************************************************************************
- * Copyright (C) 2018 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
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "event.h"
namespace Quotient {
-class DirectChatEvent : public Event {
+class QUOTIENT_API DirectChatEvent : public Event {
public:
- DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent)
+ QUO_EVENT(DirectChatEvent, "m.direct")
- explicit DirectChatEvent(const QJsonObject& obj) : Event(typeId(), obj) {}
+ using Event::Event;
QMultiHash<QString, QString> usersToDirectChats() const;
};
-REGISTER_EVENT_TYPE(DirectChatEvent)
} // namespace Quotient
diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp
index dccfa540..540594d1 100644
--- a/lib/events/encryptedevent.cpp
+++ b/lib/events/encryptedevent.cpp
@@ -1,32 +1,69 @@
-#include "encryptedevent.h"
+// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
+// SPDX-License-Identifier: LGPL-2.1-or-later
-#include "room.h"
+#include "encryptedevent.h"
+#include "e2ee/e2ee.h"
+#include "logging.h"
using namespace Quotient;
-using namespace QtOlm;
-EncryptedEvent::EncryptedEvent(const QJsonObject& ciphertext,
+EncryptedEvent::EncryptedEvent(const QJsonObject& ciphertexts,
const QString& senderKey)
- : RoomEvent(typeId(), matrixTypeId(),
- { { AlgorithmKeyL, OlmV1Curve25519AesSha2AlgoKey },
- { CiphertextKeyL, ciphertext },
- { SenderKeyKeyL, senderKey } })
+ : RoomEvent(basicJson(TypeId, { { AlgorithmKeyL, OlmV1Curve25519AesSha2AlgoKey },
+ { CiphertextKeyL, ciphertexts },
+ { SenderKeyKeyL, senderKey } }))
{}
-EncryptedEvent::EncryptedEvent(QByteArray ciphertext, const QString& senderKey,
+EncryptedEvent::EncryptedEvent(const QByteArray& ciphertext,
+ const QString& senderKey,
const QString& deviceId, const QString& sessionId)
- : RoomEvent(typeId(), matrixTypeId(),
- {
- { AlgorithmKeyL, MegolmV1AesSha2AlgoKey },
- { CiphertextKeyL, QString(ciphertext) },
- { DeviceIdKeyL, deviceId },
- { SenderKeyKeyL, senderKey },
- { SessionIdKeyL, sessionId },
- })
+ : RoomEvent(basicJson(TypeId, { { AlgorithmKeyL, MegolmV1AesSha2AlgoKey },
+ { CiphertextKeyL, QString(ciphertext) },
+ { DeviceIdKeyL, deviceId },
+ { SenderKeyKeyL, senderKey },
+ { SessionIdKeyL, sessionId } }))
{}
EncryptedEvent::EncryptedEvent(const QJsonObject& obj)
- : RoomEvent(typeId(), obj)
+ : RoomEvent(obj)
{
qCDebug(E2EE) << "Encrypted event from" << senderId();
}
+
+QString EncryptedEvent::algorithm() const
+{
+ const auto algo = contentPart<QString>(AlgorithmKeyL);
+ if (!isSupportedAlgorithm(algo))
+ qWarning(MAIN) << "The EncryptedEvent's algorithm" << algo
+ << "is not supported";
+
+ return algo;
+}
+
+RoomEventPtr EncryptedEvent::createDecrypted(const QString &decrypted) const
+{
+ auto eventObject = QJsonDocument::fromJson(decrypted.toUtf8()).object();
+ eventObject["event_id"] = id();
+ eventObject["sender"] = senderId();
+ eventObject["origin_server_ts"] = originTimestamp().toMSecsSinceEpoch();
+ if (const auto relatesToJson = contentPart<QJsonObject>("m.relates_to"_ls);
+ !relatesToJson.isEmpty()) {
+ auto content = eventObject["content"].toObject();
+ content["m.relates_to"] = relatesToJson;
+ eventObject["content"] = content;
+ }
+ if (const auto redactsJson = unsignedPart<QString>("redacts"_ls);
+ !redactsJson.isEmpty()) {
+ auto unsign = eventObject["unsigned"].toObject();
+ unsign["redacts"] = redactsJson;
+ eventObject["unsigned"] = unsign;
+ }
+ return loadEvent<RoomEvent>(eventObject);
+}
+
+void EncryptedEvent::setRelation(const QJsonObject& relation)
+{
+ auto content = contentJson();
+ content["m.relates_to"] = relation;
+ editJson()["content"] = content;
+}
diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h
index 235b2aa4..e24e5745 100644
--- a/lib/events/encryptedevent.h
+++ b/lib/events/encryptedevent.h
@@ -1,10 +1,17 @@
+// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
#pragma once
-#include "e2ee.h"
#include "roomevent.h"
namespace Quotient {
-class Room;
+
+constexpr auto CiphertextKeyL = "ciphertext"_ls;
+constexpr auto SenderKeyKeyL = "sender_key"_ls;
+constexpr auto DeviceIdKeyL = "device_id"_ls;
+constexpr auto SessionIdKeyL = "session_id"_ls;
+
/*
* While the specification states:
*
@@ -23,44 +30,39 @@ class Room;
* in general. It's possible, because RoomEvent interface is similar to Event's
* one and doesn't add new restrictions, just provides additional features.
*/
-class EncryptedEvent : public RoomEvent {
- Q_GADGET
+class QUOTIENT_API EncryptedEvent : public RoomEvent {
public:
- DEFINE_EVENT_TYPEID("m.room.encrypted", EncryptedEvent)
+ QUO_EVENT(EncryptedEvent, "m.room.encrypted")
/* In case with Olm, the encrypted content of the event is
* a map from the recipient Curve25519 identity key to ciphertext
* information */
- explicit EncryptedEvent(const QJsonObject& ciphertext,
+ explicit EncryptedEvent(const QJsonObject& ciphertexts,
const QString& senderKey);
/* In case with Megolm, device_id and session_id are required */
- explicit EncryptedEvent(QByteArray ciphertext, const QString& senderKey,
- const QString& deviceId, const QString& sessionId);
+ explicit EncryptedEvent(const QByteArray& ciphertext,
+ const QString& senderKey, const QString& deviceId,
+ const QString& sessionId);
explicit EncryptedEvent(const QJsonObject& obj);
- QString algorithm() const
- {
- QString algo = content<QString>(AlgorithmKeyL);
- if (!SupportedAlgorithms.contains(algo)) {
- qWarning(MAIN) << "The EncryptedEvent's algorithm" << algo
- << "is not supported";
- }
- return algo;
- }
+ QString algorithm() const;
QByteArray ciphertext() const
{
- return content<QString>(CiphertextKeyL).toLatin1();
+ return contentPart<QString>(CiphertextKeyL).toLatin1();
}
QJsonObject ciphertext(const QString& identityKey) const
{
- return content<QJsonObject>(CiphertextKeyL).value(identityKey).toObject();
+ return contentPart<QJsonObject>(CiphertextKeyL)
+ .value(identityKey)
+ .toObject();
}
- QString senderKey() const { return content<QString>(SenderKeyKeyL); }
+ QString senderKey() const { return contentPart<QString>(SenderKeyKeyL); }
/* device_id and session_id are required with Megolm */
- QString deviceId() const { return content<QString>(DeviceIdKeyL); }
- QString sessionId() const { return content<QString>(SessionIdKeyL); }
-};
-REGISTER_EVENT_TYPE(EncryptedEvent)
+ QString deviceId() const { return contentPart<QString>(DeviceIdKeyL); }
+ QString sessionId() const { return contentPart<QString>(SessionIdKeyL); }
+ RoomEventPtr createDecrypted(const QString &decrypted) const;
+ void setRelation(const QJsonObject& relation);
+};
} // namespace Quotient
diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp
index f1bde621..b1b04984 100644
--- a/lib/events/encryptionevent.cpp
+++ b/lib/events/encryptionevent.cpp
@@ -1,45 +1,53 @@
+// SPDX-FileCopyrightText: 2017 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
#include "encryptionevent.h"
+#include "logging.h"
-#include "e2ee.h"
+#include "e2ee/e2ee.h"
-#include <array>
+using namespace Quotient;
-namespace Quotient {
-static const std::array<QString, 1> encryptionStrings = {
- { MegolmV1AesSha2AlgoKey }
-};
+static constexpr std::array encryptionStrings { MegolmV1AesSha2AlgoKey };
template <>
-struct JsonConverter<EncryptionType> {
- static EncryptionType load(const QJsonValue& jv)
- {
- const auto& encryptionString = jv.toString();
- for (auto it = encryptionStrings.begin(); it != encryptionStrings.end();
- ++it)
- if (encryptionString == *it)
- return EncryptionType(it - encryptionStrings.begin());
-
- if (!encryptionString.isEmpty())
- qCWarning(EVENTS) << "Unknown EncryptionType: " << encryptionString;
- return EncryptionType::Undefined;
- }
-};
-} // namespace Quotient
-
-using namespace Quotient;
+EncryptionType Quotient::fromJson(const QJsonValue& jv)
+{
+ const auto& encryptionString = jv.toString();
+ for (auto it = encryptionStrings.begin(); it != encryptionStrings.end();
+ ++it)
+ if (encryptionString == *it)
+ return EncryptionType(it - encryptionStrings.begin());
+
+ if (!encryptionString.isEmpty())
+ qCWarning(EVENTS) << "Unknown EncryptionType: " << encryptionString;
+ return EncryptionType::Undefined;
+}
EncryptionEventContent::EncryptionEventContent(const QJsonObject& json)
- : encryption(fromJson<EncryptionType>(json[AlgorithmKeyL]))
+ : encryption(fromJson<Quotient::EncryptionType>(json[AlgorithmKeyL]))
, algorithm(sanitized(json[AlgorithmKeyL].toString()))
- , rotationPeriodMs(json[RotationPeriodMsKeyL].toInt(604800000))
- , rotationPeriodMsgs(json[RotationPeriodMsgsKeyL].toInt(100))
-{}
+{
+ // NB: fillFromJson only fills the variable if the JSON key exists
+ fillFromJson<int>(json[RotationPeriodMsKeyL], rotationPeriodMs);
+ fillFromJson<int>(json[RotationPeriodMsgsKeyL], rotationPeriodMsgs);
+}
+
+EncryptionEventContent::EncryptionEventContent(Quotient::EncryptionType et)
+ : encryption(et)
+{
+ if(encryption != Quotient::EncryptionType::Undefined) {
+ algorithm = encryptionStrings[static_cast<size_t>(encryption)];
+ }
+}
-void EncryptionEventContent::fillJson(QJsonObject* o) const
+QJsonObject EncryptionEventContent::toJson() const
{
- Q_ASSERT(o);
- if (encryption != EncryptionType::Undefined)
- o->insert(AlgorithmKey, algorithm);
- o->insert(RotationPeriodMsKey, rotationPeriodMs);
- o->insert(RotationPeriodMsgsKey, rotationPeriodMsgs);
+ QJsonObject o;
+ if (encryption != Quotient::EncryptionType::Undefined)
+ o.insert(AlgorithmKey, algorithm);
+ o.insert(RotationPeriodMsKey, rotationPeriodMs);
+ o.insert(RotationPeriodMsgsKey, rotationPeriodMsgs);
+ return o;
}
diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h
index cbd3ba4a..4bf7459c 100644
--- a/lib/events/encryptionevent.h
+++ b/lib/events/encryptionevent.h
@@ -1,73 +1,47 @@
-/******************************************************************************
- * Copyright (C) 2017 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
- */
+// SPDX-FileCopyrightText: 2017 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
-#include "eventcontent.h"
+#include "quotient_common.h"
#include "stateevent.h"
namespace Quotient {
-class EncryptionEventContent : public EventContent::Base {
+class QUOTIENT_API EncryptionEventContent {
public:
- enum EncryptionType : size_t { MegolmV1AesSha2 = 0, Undefined };
+ using EncryptionType
+ [[deprecated("Use Quotient::EncryptionType instead")]] =
+ Quotient::EncryptionType;
- explicit EncryptionEventContent(EncryptionType et = Undefined)
- : encryption(et)
- {}
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ QUO_IMPLICIT EncryptionEventContent(Quotient::EncryptionType et);
explicit EncryptionEventContent(const QJsonObject& json);
- EncryptionType encryption;
- QString algorithm;
- int rotationPeriodMs;
- int rotationPeriodMsgs;
+ QJsonObject toJson() const;
-protected:
- void fillJson(QJsonObject* o) const override;
+ Quotient::EncryptionType encryption;
+ QString algorithm {};
+ int rotationPeriodMs = 604'800'000;
+ int rotationPeriodMsgs = 100;
};
-using EncryptionType = EncryptionEventContent::EncryptionType;
-
-class EncryptionEvent : public StateEvent<EncryptionEventContent> {
- Q_GADGET
+class QUOTIENT_API EncryptionEvent
+ : public KeylessStateEventBase<EncryptionEvent, EncryptionEventContent> {
public:
- DEFINE_EVENT_TYPEID("m.room.encryption", EncryptionEvent)
-
- using EncryptionType = EncryptionEventContent::EncryptionType;
+ QUO_EVENT(EncryptionEvent, "m.room.encryption")
- explicit EncryptionEvent(const QJsonObject& obj = {}) // TODO: apropriate
- // default value
- : StateEvent(typeId(), obj)
- {}
- template <typename... ArgTs>
- EncryptionEvent(ArgTs&&... contentArgs)
- : StateEvent(typeId(), matrixTypeId(), QString(),
- std::forward<ArgTs>(contentArgs)...)
- {}
+ using EncryptionType
+ [[deprecated("Use Quotient::EncryptionType instead")]] =
+ Quotient::EncryptionType;
- EncryptionType encryption() const { return content().encryption; }
+ using KeylessStateEventBase::KeylessStateEventBase;
+ Quotient::EncryptionType encryption() const { return content().encryption; }
QString algorithm() const { return content().algorithm; }
int rotationPeriodMs() const { return content().rotationPeriodMs; }
int rotationPeriodMsgs() const { return content().rotationPeriodMsgs; }
-private:
- Q_ENUM(EncryptionType)
+ bool useEncryption() const { return !algorithm().isEmpty(); }
};
-
-REGISTER_EVENT_TYPE(EncryptionEvent)
} // namespace Quotient
diff --git a/lib/events/event.cpp b/lib/events/event.cpp
index 7b34114d..da7de919 100644
--- a/lib/events/event.cpp
+++ b/lib/events/event.cpp
@@ -1,48 +1,54 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "event.h"
+#include "callevents.h"
#include "logging.h"
+#include "stateevent.h"
#include <QtCore/QJsonDocument>
using namespace Quotient;
-event_type_t EventTypeRegistry::initializeTypeId(event_mtype_t matrixTypeId)
-{
- const auto id = get().eventTypes.size();
- get().eventTypes.push_back(matrixTypeId);
- if (strncmp(matrixTypeId, "", 1) == 0)
- qDebug(EVENTS) << "Initialized unknown event type with id" << id;
- else
- qDebug(EVENTS) << "Initialized event type" << matrixTypeId << "with id"
- << id;
- return id;
-}
+QString EventTypeRegistry::getMatrixType(event_type_t typeId) { return typeId; }
-QString EventTypeRegistry::getMatrixType(event_type_t typeId)
+void AbstractEventMetaType::addDerived(AbstractEventMetaType* newType)
{
- return typeId < get().eventTypes.size() ? get().eventTypes[typeId]
- : QString();
+ if (const auto existing =
+ std::find_if(derivedTypes.cbegin(), derivedTypes.cend(),
+ [&newType](const AbstractEventMetaType* t) {
+ return t->matrixId == newType->matrixId;
+ });
+ existing != derivedTypes.cend())
+ {
+ if (*existing == newType)
+ return;
+ // Two different metatype objects claim the same Matrix type id; this
+ // is not normal, so give as much information as possible to diagnose
+ if ((*existing)->className == newType->className) {
+ qCritical(EVENTS)
+ << newType->className << "claims" << newType->matrixId
+ << "repeatedly; check that it's exported across translation "
+ "units or shared objects";
+ Q_ASSERT(false); // That situation is plain wrong
+ return; // So maybe std::terminate() even?
+ }
+ qWarning(EVENTS).nospace()
+ << newType->matrixId << " is already mapped to "
+ << (*existing)->className << " before " << newType->className
+ << "; unless the two have different isValid() conditions, the "
+ "latter class will never be used";
+ }
+ derivedTypes.emplace_back(newType);
+ qDebug(EVENTS).nospace()
+ << newType->matrixId << " -> " << newType->className << "; "
+ << derivedTypes.size() << " derived type(s) registered for "
+ << className;
}
-Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json)
+Event::Event(const QJsonObject& json)
+ : _json(json)
{
if (!json.contains(ContentKeyL)
&& !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) {
@@ -51,29 +57,26 @@ Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json)
}
}
-Event::Event(Type type, event_mtype_t matrixType, const QJsonObject& contentJson)
- : Event(type, basicEventJson(matrixType, contentJson))
-{}
-
Event::~Event() = default;
QString Event::matrixType() const { return fullJson()[TypeKeyL].toString(); }
QByteArray Event::originalJson() const { return QJsonDocument(_json).toJson(); }
-// On const below: this is to catch accidental attempts to change event JSON
-// NOLINTNEXTLINE(readability-const-return-type)
const QJsonObject Event::contentJson() const
{
return fullJson()[ContentKeyL].toObject();
}
-// NOLINTNEXTLINE(readability-const-return-type)
const QJsonObject Event::unsignedJson() const
{
return fullJson()[UnsignedKeyL].toObject();
}
+bool Event::isStateEvent() const { return is<StateEvent>(); }
+
+bool Event::isCallEvent() const { return is<CallEvent>(); }
+
void Event::dumpTo(QDebug dbg) const
{
dbg << QJsonDocument(contentJson()).toJson(QJsonDocument::Compact);
diff --git a/lib/events/event.h b/lib/events/event.h
index 6c8961ad..0abef1f0 100644
--- a/lib/events/event.h
+++ b/lib/events/event.h
@@ -1,32 +1,14 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "converters.h"
-#include "logging.h"
-
-#ifdef ENABLE_EVENTTYPE_ALIAS
-# define USE_EVENTTYPE_ALIAS 1
-#endif
+#include "function_traits.h"
+#include "single_key_value.h"
namespace Quotient {
-// === event_ptr_tt<> and type casting facilities ===
+// === event_ptr_tt<> and basic type casting facilities ===
template <typename EventT>
using event_ptr_tt = std::unique_ptr<EventT>;
@@ -45,198 +27,288 @@ inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr)
return static_cast<TargetEventT*>(rawPtr(ptr));
}
-/// Re-wrap a smart pointer to base into a smart pointer to derived
-template <typename TargetT, typename SourceT>
-[[deprecated("Consider using eventCast() or visit() instead")]]
-inline event_ptr_tt<TargetT> ptrCast(event_ptr_tt<SourceT>&& ptr)
-{
- return std::unique_ptr<TargetT>(static_cast<TargetT*>(ptr.release()));
-}
-
// === Standard Matrix key names and basicEventJson() ===
-static const auto TypeKey = QStringLiteral("type");
-static const auto BodyKey = QStringLiteral("body");
-static const auto ContentKey = QStringLiteral("content");
-static const auto EventIdKey = QStringLiteral("event_id");
-static const auto UnsignedKey = QStringLiteral("unsigned");
-static const auto StateKeyKey = QStringLiteral("state_key");
-static const auto TypeKeyL = "type"_ls;
-static const auto BodyKeyL = "body"_ls;
-static const auto ContentKeyL = "content"_ls;
-static const auto EventIdKeyL = "event_id"_ls;
-static const auto UnsignedKeyL = "unsigned"_ls;
-static const auto RedactedCauseKeyL = "redacted_because"_ls;
-static const auto PrevContentKeyL = "prev_content"_ls;
-static const auto StateKeyKeyL = "state_key"_ls;
-
-/// Make a minimal correct Matrix event JSON
-template <typename StrT>
-inline QJsonObject basicEventJson(StrT matrixType, const QJsonObject& content)
-{
- return { { TypeKey, std::forward<StrT>(matrixType) },
- { ContentKey, content } };
-}
-
-// === Event types and event types registry ===
-
-using event_type_t = size_t;
-using event_mtype_t = const char*;
+constexpr auto TypeKeyL = "type"_ls;
+constexpr auto BodyKeyL = "body"_ls;
+constexpr auto ContentKeyL = "content"_ls;
+constexpr auto EventIdKeyL = "event_id"_ls;
+constexpr auto SenderKeyL = "sender"_ls;
+constexpr auto RoomIdKeyL = "room_id"_ls;
+constexpr auto UnsignedKeyL = "unsigned"_ls;
+constexpr auto RedactedCauseKeyL = "redacted_because"_ls;
+constexpr auto PrevContentKeyL = "prev_content"_ls;
+constexpr auto StateKeyKeyL = "state_key"_ls;
+const QString TypeKey { TypeKeyL };
+const QString BodyKey { BodyKeyL };
+const QString ContentKey { ContentKeyL };
+const QString EventIdKey { EventIdKeyL };
+const QString SenderKey { SenderKeyL };
+const QString RoomIdKey { RoomIdKeyL };
+const QString UnsignedKey { UnsignedKeyL };
+const QString StateKeyKey { StateKeyKeyL };
+
+using event_type_t = QLatin1String;
+
+// TODO: Remove in 0.8
+struct QUOTIENT_API EventTypeRegistry {
+ [[deprecated("event_type_t is a string since libQuotient 0.7, use it directly instead")]]
+ static QString getMatrixType(event_type_t typeId);
-class EventTypeRegistry {
-public:
+ EventTypeRegistry() = delete;
~EventTypeRegistry() = default;
+ Q_DISABLE_COPY_MOVE(EventTypeRegistry)
+};
- static event_type_t initializeTypeId(event_mtype_t matrixTypeId);
+// === EventMetaType ===
- template <typename EventT>
- static inline event_type_t initializeTypeId()
- {
- return initializeTypeId(EventT::matrixTypeId());
- }
+class Event;
- static QString getMatrixType(event_type_t typeId);
+// TODO: move over to std::derived_from<Event> once it's available everywhere
+template <typename EventT, typename BaseEventT = Event>
+concept EventClass = std::is_base_of_v<BaseEventT, EventT>;
-private:
- EventTypeRegistry() = default;
- Q_DISABLE_COPY(EventTypeRegistry)
- DISABLE_MOVE(EventTypeRegistry)
+template <EventClass EventT>
+bool is(const Event& e);
- static EventTypeRegistry& get()
+//! \brief The base class for event metatypes
+//!
+//! You should not normally have to use this directly, unless you need to devise
+//! a whole new kind of event metatypes.
+class QUOTIENT_API AbstractEventMetaType {
+public:
+ // The public fields here are const and are not to be changeable anyway.
+ // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
+ const char* const className;
+ const event_type_t matrixId;
+ const AbstractEventMetaType* const baseType = nullptr;
+ // NOLINTEND(misc-non-private-member-variables-in-classes)
+
+ explicit AbstractEventMetaType(const char* className)
+ : className(className)
+ {}
+ explicit AbstractEventMetaType(const char* className, event_type_t matrixId,
+ AbstractEventMetaType& nearestBase)
+ : className(className), matrixId(matrixId), baseType(&nearestBase)
{
- static EventTypeRegistry etr;
- return etr;
+ nearestBase.addDerived(this);
}
- std::vector<event_mtype_t> eventTypes;
-};
-
-template <>
-inline event_type_t EventTypeRegistry::initializeTypeId<void>()
-{
- return initializeTypeId("");
-}
+ void addDerived(AbstractEventMetaType *newType);
-template <typename EventT>
-struct EventTypeTraits {
- static event_type_t id()
- {
- static const auto id = EventTypeRegistry::initializeTypeId<EventT>();
- return id;
- }
-};
+ virtual ~AbstractEventMetaType() = default;
-template <typename EventT>
-inline event_type_t typeId()
-{
- return EventTypeTraits<std::decay_t<EventT>>::id();
-}
+protected:
+ // Allow template specialisations to call into one another
+ template <class EventT>
+ friend class EventMetaType;
-inline event_type_t unknownEventTypeId() { return typeId<void>(); }
+ // The returned value indicates whether a generic object has to be created
+ // on the top level when `event` is empty, instead of returning nullptr
+ virtual bool doLoadFrom(const QJsonObject& fullJson, const QString& type,
+ Event*& event) const = 0;
-// === EventFactory ===
+private:
+ std::vector<const AbstractEventMetaType*> derivedTypes{};
+ Q_DISABLE_COPY_MOVE(AbstractEventMetaType)
+};
-/** Create an event of arbitrary type from its arguments */
-template <typename EventT, typename... ArgTs>
-inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args)
+// Any event metatype is unique (note Q_DISABLE_COPY_MOVE above) so can be
+// identified by its address
+inline bool operator==(const AbstractEventMetaType& lhs,
+ const AbstractEventMetaType& rhs)
{
- return std::make_unique<EventT>(std::forward<ArgTs>(args)...);
+ return &lhs == &rhs;
}
-template <typename BaseEventT>
-class EventFactory {
+//! \brief A family of event meta-types to load and match events
+//!
+//! TL;DR for the loadFrom() story:
+//! - for base event types, use QUO_BASE_EVENT and, if you have additional
+//! validation (e.g., JSON has to contain a certain key - see StateEvent
+//! for a real example), define it in the static EventT::isValid() member
+//! function accepting QJsonObject and returning bool.
+//! - for leaf (specific) event types - simply use QUO_EVENT and it will do
+//! everything necessary, including the TypeId definition.
+//! \sa QUO_EVENT, QUO_BASE_EVENT
+template <class EventT>
+class QUOTIENT_API EventMetaType : public AbstractEventMetaType {
+ // Above: can't constrain EventT to be EventClass because it's incomplete
+ // at the point of EventMetaType<EventT> instantiation.
public:
- template <typename FnT>
- static auto addMethod(FnT&& method)
- {
- factories().emplace_back(std::forward<FnT>(method));
- return 0;
- }
-
- /** Chain two type factories
- * Adds the factory class of EventT2 (EventT2::factory_t) to
- * the list in factory class of EventT1 (EventT1::factory_t) so
- * that when EventT1::factory_t::make() is invoked, types of
- * EventT2 factory are looked through as well. This is used
- * to include RoomEvent types into the more general Event factory,
- * and state event types into the RoomEvent factory.
- */
- template <typename EventT>
- static auto chainFactory()
+ using AbstractEventMetaType::AbstractEventMetaType;
+
+ //! \brief Try to load an event from JSON, with dynamic type resolution
+ //!
+ //! The generic logic defined in this class template and invoked applies to
+ //! all event types defined in the library and boils down to the following:
+ //! 1.
+ //! a. If EventT has TypeId defined (which normally is a case of
+ //! all leaf - specific - event types, via QUO_EVENT macro) and
+ //! \p type doesn't exactly match it, nullptr is immediately returned.
+ //! b. In absence of TypeId, an event type is assumed to be a base;
+ //! its derivedTypes are examined, and this algorithm is applied
+ //! recursively on each.
+ //! 2. Optional validation: if EventT (or, due to the way inheritance works,
+ //! any of its base event types) has a static isValid() predicate and
+ //! the event JSON does not satisfy it, nullptr is immediately returned
+ //! to the upper level or to the loadFrom() caller. This is how existence
+ //! of `state_key` is checked in any type derived from StateEvent.
+ //! 3. If step 1b above returned non-nullptr, immediately return it.
+ //! 4.
+ //! a. If EventT::isValid() or EventT::TypeId (either, or both) exist and
+ //! are satisfied (see steps 1a and 2 above), an object of this type
+ //! is created from the passed JSON and returned. In case of a base
+ //! event type, this will be a generic (aka "unknown") event.
+ //! b. If neither exists, a generic event is only created and returned
+ //! when on the top level (i.e., outside of recursion into
+ //! derivedTypes); lower levels return nullptr instead and the type
+ //! lookup continues. The latter is a case of a derived base event
+ //! metatype (e.g. RoomEvent) called from its base event metatype
+ //! (i.e., Event). If no matching type derived from RoomEvent is found,
+ //! the nested lookup returns nullptr rather than a generic RoomEvent,
+ //! so that other types derived from Event could be examined.
+ event_ptr_tt<EventT> loadFrom(const QJsonObject& fullJson,
+ const QString& type) const
{
- return addMethod(&EventT::factory_t::make);
- }
-
- static event_ptr_tt<BaseEventT> make(const QJsonObject& json,
- const QString& matrixType)
- {
- for (const auto& f : factories())
- if (auto e = f(json, matrixType))
- return e;
- return nullptr;
+ Event* event = nullptr;
+ const bool goodEnough = doLoadFrom(fullJson, type, event);
+ if (!event && goodEnough)
+ return event_ptr_tt<EventT>{ new EventT(fullJson) };
+ return event_ptr_tt<EventT>{ static_cast<EventT*>(event) };
}
private:
- static auto& factories()
+ bool doLoadFrom(const QJsonObject& fullJson, const QString& type,
+ Event*& event) const override
{
- using inner_factory_tt = std::function<event_ptr_tt<BaseEventT>(
- const QJsonObject&, const QString&)>;
- static std::vector<inner_factory_tt> _factories {};
- return _factories;
+ if constexpr (requires { EventT::TypeId; }) {
+ if (EventT::TypeId != type)
+ return false;
+ } else {
+ for (const auto& p : derivedTypes) {
+ p->doLoadFrom(fullJson, type, event);
+ if (event) {
+ Q_ASSERT(is<EventT>(*event));
+ return false;
+ }
+ }
+ }
+ if constexpr (requires { EventT::isValid; }) {
+ if (!EventT::isValid(fullJson))
+ return false;
+ } else if constexpr (!requires { EventT::TypeId; })
+ return true; // Create a generic event object if on the top level
+ event = new EventT(fullJson);
+ return false;
}
};
-/** Add a type to its default factory
- * Adds a standard factory method (via makeEvent<>) for a given
- * type to EventT::factory_t factory class so that it can be
- * created dynamically from loadEvent<>().
- *
- * \tparam EventT the type to enable dynamic creation of
- * \return the registered type id
- * \sa loadEvent, Event::type
- */
-template <typename EventT>
-inline auto setupFactory()
+// === Event creation facilities ===
+
+//! \brief Create an event of arbitrary type from its arguments
+//!
+//! This should not be used to load events from JSON - use loadEvent() for that.
+//! \sa loadEvent
+template <EventClass EventT, typename... ArgTs>
+inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args)
{
- qDebug(EVENTS) << "Adding factory method for" << EventT::matrixTypeId();
- return EventT::factory_t::addMethod([](const QJsonObject& json,
- const QString& jsonMatrixType) {
- return EventT::matrixTypeId() == jsonMatrixType ? makeEvent<EventT>(json)
- : nullptr;
- });
+ return std::make_unique<EventT>(std::forward<ArgTs>(args)...);
}
-template <typename EventT>
-inline auto registerEventType()
+template <EventClass EventT>
+constexpr const auto& mostSpecificMetaType()
{
- // Initialise exactly once, even if this function is called twice for
- // the same type (for whatever reason - you never know the ways of
- // static initialisation is done).
- static const auto _ = setupFactory<EventT>();
- return _; // Only to facilitate usage in static initialisation
+ if constexpr (requires { EventT::MetaType; })
+ return EventT::MetaType;
+ else
+ return EventT::BaseMetaType;
}
+//! \brief Create an event with proper type from a JSON object
+//!
+//! Use this factory template to detect the type from the JSON object
+//! contents (the detected event type should derive from the template
+//! parameter type) and create an event object of that type.
+template <EventClass EventT>
+inline event_ptr_tt<EventT> loadEvent(const QJsonObject& fullJson)
+{
+ return mostSpecificMetaType<EventT>().loadFrom(
+ fullJson, fullJson[TypeKeyL].toString());
+}
+
+//! \brief Create an event from a type string and content JSON
+//!
+//! Use this template to resolve the C++ type from the Matrix type string in
+//! \p matrixType and create an event of that type by passing all parameters
+//! to BaseEventT::basicJson().
+template <EventClass EventT>
+inline event_ptr_tt<EventT> loadEvent(const QString& matrixType,
+ const auto&... otherBasicJsonParams)
+{
+ return mostSpecificMetaType<EventT>().loadFrom(
+ EventT::basicJson(matrixType, otherBasicJsonParams...), matrixType);
+}
+
+template <EventClass EventT>
+struct JsonConverter<event_ptr_tt<EventT>>
+ : JsonObjectUnpacker<event_ptr_tt<EventT>> {
+ // No dump() to avoid any ambiguity on whether a given export to JSON uses
+ // fullJson() or only contentJson()
+ using JsonObjectUnpacker<event_ptr_tt<EventT>>::load;
+ static auto load(const QJsonObject& jo)
+ {
+ return loadEvent<EventT>(jo);
+ }
+};
+
// === Event ===
-class Event {
- Q_GADGET
- Q_PROPERTY(Type type READ type CONSTANT)
- Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT)
+class QUOTIENT_API Event {
public:
using Type = event_type_t;
- using factory_t = EventFactory<Event>;
+ static inline EventMetaType<Event> BaseMetaType { "Event" };
+ virtual const AbstractEventMetaType& metaType() const
+ {
+ return BaseMetaType;
+ }
- explicit Event(Type type, const QJsonObject& json);
- explicit Event(Type type, event_mtype_t matrixType,
- const QJsonObject& contentJson = {});
Q_DISABLE_COPY(Event)
- Event(Event&&) = default;
+ Event(Event&&) noexcept = default;
Event& operator=(Event&&) = delete;
virtual ~Event();
- Type type() const { return _type; }
+ /// Make a minimal correct Matrix event JSON
+ static QJsonObject basicJson(const QString& matrixType,
+ const QJsonObject& content)
+ {
+ return { { TypeKey, matrixType }, { ContentKey, content } };
+ }
+
+ //! \brief Event Matrix type, as identified by its metatype object
+ //!
+ //! For generic/unknown events it will contain a descriptive/generic string
+ //! defined by the respective base event type (that can be empty).
+ //! \sa matrixType
+ Type type() const { return metaType().matrixId; }
+
+ //! \brief Exact Matrix type stored in JSON
+ //!
+ //! Coincides with the result of type() (but is slower) for events defined
+ //! in C++ (not necessarily in the library); for generic/unknown events
+ //! the returned value will be different.
QString matrixType() const;
+
+ template <EventClass EventT>
+ bool is() const
+ {
+ return Quotient::is<EventT>(*this);
+ }
+
+ [[deprecated("Use fullJson() and stringify it with QJsonDocument::toJson() "
+ "or by other means")]]
QByteArray originalJson() const;
+ [[deprecated("Use fullJson() instead")]] //
QJsonObject originalJsonObject() const { return fullJson(); }
const QJsonObject& fullJson() const { return _json; }
@@ -245,148 +317,320 @@ public:
// a "content" object; but since its structure is different for
// different types, we're implementing it per-event type.
+ // NB: const return types below are meant to catch accidental attempts
+ // to change event JSON (e.g., consider contentJson()["inexistentKey"]).
+
const QJsonObject contentJson() const;
- const QJsonObject unsignedJson() const;
+
+ //! \brief Get a part of the content object, assuming a given type
+ //!
+ //! This retrieves the value under `content.<key>` from the event JSON and
+ //! then converts it to \p T using fromJson().
+ //! \sa contentJson, fromJson
+ template <typename T, typename KeyT>
+ const T contentPart(KeyT&& key) const
+ {
+ return fromJson<T>(contentJson()[std::forward<KeyT>(key)]);
+ }
template <typename T>
+ [[deprecated("Use contentPart() to get a part of the event content")]] //
T content(const QString& key) const
{
- return fromJson<T>(contentJson()[key]);
+ return contentPart<T>(key);
}
- template <typename T>
- T content(QLatin1String key) const
+ const QJsonObject unsignedJson() const;
+
+ //! \brief Get a part of the unsigned object, assuming a given type
+ //!
+ //! This retrieves the value under `unsigned.<key>` from the event JSON and
+ //! then converts it to \p T using fromJson().
+ //! \sa unsignedJson, fromJson
+ template <typename T, typename KeyT>
+ const T unsignedPart(KeyT&& key) const
{
- return fromJson<T>(contentJson()[key]);
+ return fromJson<T>(unsignedJson()[std::forward<KeyT>(key)]);
}
- friend QDebug operator<<(QDebug dbg, const Event& e)
+ friend QUOTIENT_API QDebug operator<<(QDebug dbg, const Event& e)
{
QDebugStateSaver _dss { dbg };
- dbg.noquote().nospace() << e.matrixType() << '(' << e.type() << "): ";
+ dbg.noquote().nospace()
+ << e.matrixType() << '(' << e.metaType().className << "): ";
e.dumpTo(dbg);
return dbg;
}
- virtual bool isStateEvent() const { return false; }
- virtual bool isCallEvent() const { return false; }
- virtual void dumpTo(QDebug dbg) const;
+ // State events are quite special in Matrix; so isStateEvent() is here,
+ // as an exception. For other base events, Event::is<>() and
+ // Quotient::is<>() should be used; don't add is* methods here
+ bool isStateEvent() const;
+ [[deprecated("Use is<CallEvent>() instead")]] bool isCallEvent() const;
protected:
+ friend class EventMetaType<Event>; // To access the below constructor
+
+ explicit Event(const QJsonObject& json);
+
QJsonObject& editJson() { return _json; }
+ virtual void dumpTo(QDebug dbg) const;
private:
- Type _type;
QJsonObject _json;
};
using EventPtr = event_ptr_tt<Event>;
-template <typename EventT>
+template <EventClass EventT>
using EventsArray = std::vector<event_ptr_tt<EventT>>;
using Events = EventsArray<Event>;
-// === Macros used with event class definitions ===
+// === Facilities for event class definitions ===
+
+//! \brief A template base class to derive your event type from
+//!
+//! This simple class template generates commonly used event constructor
+//! signatures and the content() method with the appropriate return type.
+//! The generic version here is only used with non-trivial \p ContentT (if you
+//! don't need to create an event from its content structure, just go and derive
+//! straight from the respective \p EventBaseT instead of using EventTemplate);
+//! specialisations may override that and provide useful semantics even without
+//! \p ContentT (see EventTemplate<CallEvent>, e.g.).
+//!
+//! The template uses CRTP to pick the event type id from the actual class;
+//! it will fail to compile if \p EventT doesn't provide TypeId. It also uses
+//! the base event type's basicJson(); if you need extra keys to be inserted
+//! you may want to bypass this template as writing the code to that effect in
+//! your class will likely be clearer and more concise.
+//! \sa https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
+//! \sa DEFINE_SIMPLE_EVENT
+template <typename EventT, EventClass BaseEventT, typename ContentT = void>
+class EventTemplate : public BaseEventT {
+ // Above: can't constrain EventT to be EventClass because it's incomplete
+ // by CRTP definition.
+public:
+ static_assert(
+ !std::is_same_v<ContentT, void>,
+ "If you see this, you tried to use EventTemplate with the default"
+ " ContentT type, which is void. This default is only used with explicit"
+ " specialisations (see CallEvent, e.g.). Otherwise, if you don't intend"
+ " to use the content part of EventTemplate then you don't need"
+ " EventTemplate; just use the base event class directly");
+ using content_type = ContentT;
+
+ explicit EventTemplate(const QJsonObject& json)
+ : BaseEventT(json)
+ {}
+ explicit EventTemplate(const ContentT& c)
+ : BaseEventT(EventT::basicJson(EventT::TypeId, toJson(c)))
+ {}
+
+ ContentT content() const { return fromJson<ContentT>(this->contentJson()); }
+};
-// This macro should be used in a public section of an event class to
-// provide matrixTypeId() and typeId().
-#define DEFINE_EVENT_TYPEID(_Id, _Type) \
- static constexpr event_mtype_t matrixTypeId() { return _Id; } \
- static auto typeId() { return Quotient::typeId<_Type>(); } \
+//! \brief Supply event metatype information in base event types
+//!
+//! Use this macro in a public section of your base event class to provide
+//! type identity and enable dynamic loading of generic events of that type.
+//! Do _not_ add this macro if your class is an intermediate wrapper and is not
+//! supposed to be instantiated on its own. Provides BaseMetaType static field
+//! initialised by parameters passed to the macro, and a metaType() override
+//! pointing to that BaseMetaType.
+//! \sa EventMetaType, EventMetaType::SuppressLoadDerived
+#define QUO_BASE_EVENT(CppType_, ...) \
+ friend class EventMetaType<CppType_>; \
+ static inline EventMetaType<CppType_> BaseMetaType{ \
+ #CppType_ __VA_OPT__(,) __VA_ARGS__ }; \
+ const AbstractEventMetaType& metaType() const override \
+ { \
+ return BaseMetaType; \
+ } \
// End of macro
-// This macro should be put after an event class definition (in .h or .cpp)
-// to enable its deserialisation from a /sync and other
-// polymorphic event arrays
-#define REGISTER_EVENT_TYPE(_Type) \
- namespace { \
- [[maybe_unused]] static const auto _factoryAdded##_Type = \
- registerEventType<_Type>(); \
- } \
+//! Supply event metatype information in (specific) event types
+//!
+//! Use this macro in a public section of your event class to provide type
+//! identity and enable dynamic loading of generic events of that type.
+//! Do _not_ use this macro if your class is an intermediate wrapper and is not
+//! supposed to be instantiated on its own. Provides MetaType static field
+//! initialised as described below; a metaType() override pointing to it; and
+//! the TypeId static field that is equal to MetaType.matrixId.
+//!
+//! The first two macro parameters are used as the first two EventMetaType
+//! constructor parameters; the third EventMetaType parameter is always
+//! BaseMetaType; and additional base types can be passed in extra macro
+//! parameters if you need to include the same event type in more than one
+//! event factory hierarchy (e.g., EncryptedEvent).
+//! \sa EventMetaType
+#define QUO_EVENT(CppType_, MatrixType_, ...) \
+ static inline const auto& TypeId = MatrixType_##_ls; \
+ friend class EventMetaType<CppType_>; \
+ static inline const EventMetaType<CppType_> MetaType{ \
+ #CppType_, TypeId, BaseMetaType __VA_OPT__(,) __VA_ARGS__ \
+ }; \
+ const AbstractEventMetaType& metaType() const override \
+ { \
+ return MetaType; \
+ } \
+ [[deprecated("Use " #CppType_ "::TypeId directly instead")]] \
+ static constexpr const char* matrixTypeId() { return MatrixType_; } \
+ [[deprecated("Use " #CppType_ "::TypeId directly instead")]] \
+ static event_type_t typeId() { return TypeId; } \
// End of macro
-// === is<>(), eventCast<>() and visit<>() ===
+//! \deprecated This is the old name for what is now known as QUO_EVENT
+#define DEFINE_EVENT_TYPEID(Type_, Id_) QUO_EVENT(Type_, Id_)
-template <typename EventT>
-inline bool is(const Event& e)
-{
- return e.type() == typeId<EventT>();
-}
+#define QUO_CONTENT_GETTER_X(PartType_, PartName_, JsonKey_) \
+ PartType_ PartName_() const \
+ { \
+ static const auto PartName_##JsonKey = JsonKey_; \
+ return contentPart<PartType_>(PartName_##JsonKey); \
+ }
-inline bool isUnknown(const Event& e)
+//! \brief Define an inline method obtaining a content part
+//!
+//! This macro adds a const method that extracts a JSON value at the key
+//! <tt>toSnakeCase(PartName_)</tt> (sic) and converts it to the type
+//! \p PartType_. Effectively, the generated method is an equivalent of
+//! \code
+//! contentPart<PartType_>(Quotient::toSnakeCase(#PartName_##_ls));
+//! \endcode
+#define QUO_CONTENT_GETTER(PartType_, PartName_) \
+ QUO_CONTENT_GETTER_X(PartType_, PartName_, toSnakeCase(#PartName_##_ls))
+
+//! \deprecated This macro was used after an event class definition
+//! to enable its dynamic loading; it is completely superseded by QUO_EVENT
+#define REGISTER_EVENT_TYPE(Type_)
+
+/// \brief Define a new event class with a single key-value pair in the content
+///
+/// This macro defines a new event class \p Name_ derived from \p Base_,
+/// with Matrix event type \p TypeId_, providing a getter named \p GetterName_
+/// for a single value of type \p ValueType_ inside the event content.
+/// To retrieve the value the getter uses a JSON key name that corresponds to
+/// its own (getter's) name but written in snake_case. \p GetterName_ must be
+/// in camelCase, no quotes (an identifier, not a literal).
+#define DEFINE_SIMPLE_EVENT(Name_, Base_, TypeId_, ValueType_, GetterName_, \
+ JsonKey_) \
+ constexpr auto Name_##ContentKey = JsonKey_##_ls; \
+ class QUOTIENT_API Name_ \
+ : public EventTemplate< \
+ Name_, Base_, \
+ EventContent::SingleKeyValue<ValueType_, Name_##ContentKey>> { \
+ public: \
+ QUO_EVENT(Name_, TypeId_) \
+ using value_type = ValueType_; \
+ using EventTemplate::EventTemplate; \
+ QUO_CONTENT_GETTER_X(ValueType_, GetterName_, Name_##ContentKey) \
+ }; \
+ // End of macro
+
+// === is<>(), eventCast<>() and switchOnType<>() ===
+
+template <EventClass EventT>
+inline bool is(const Event& e)
{
- return e.type() == unknownEventTypeId();
+ if constexpr (requires { EventT::MetaType; }) {
+ return &e.metaType() == &EventT::MetaType;
+ } else {
+ const auto* p = &e.metaType();
+ do {
+ if (p == &EventT::BaseMetaType)
+ return true;
+ } while ((p = p->baseType) != nullptr);
+ return false;
+ }
}
-template <typename EventT, typename BasePtrT>
+//! \brief Cast the event pointer down in a type-safe way
+//!
+//! Checks that the event \p eptr points to actually is of the requested type
+//! and returns a (plain) pointer to the event downcast to that type. \p eptr
+//! can be either "dumb" (BaseEventT*) or "smart" (`event_ptr_tt<>`). This
+//! overload doesn't affect the event ownership - if the original pointer owns
+//! the event it must outlive the downcast pointer to keep it from dangling.
+template <EventClass EventT, typename BasePtrT>
inline auto eventCast(const BasePtrT& eptr)
-> decltype(static_cast<EventT*>(&*eptr))
{
- Q_ASSERT(eptr);
- return is<std::decay_t<EventT>>(*eptr) ? static_cast<EventT*>(&*eptr)
- : nullptr;
+ return eptr && is<std::decay_t<EventT>>(*eptr)
+ ? static_cast<EventT*>(&*eptr)
+ : nullptr;
}
-// A single generic catch-all visitor
-template <typename BaseEventT, typename FnT>
-inline auto visit(const BaseEventT& event, FnT&& visitor)
- -> decltype(visitor(event))
+//! \brief Cast the event pointer down in a type-safe way, with moving
+//!
+//! Checks that the event \p eptr points to actually is of the requested type;
+//! if (and only if) it is, releases the pointer, downcasts it to the requested
+//! event type and returns a new smart pointer wrapping the downcast one.
+//! Unlike the non-moving eventCast() overload, this one only accepts a smart
+//! pointer, and that smart pointer should be an rvalue (either a temporary,
+//! or as a result of std::move()). The ownership, respectively, is transferred
+//! to the new pointer; the original smart pointer is reset to nullptr, as is
+//! normal for `unique_ptr<>::release()`.
+//! \note If \p eptr's event type does not match \p EventT it retains ownership
+//! after calling this overload; if it is a temporary, this normally
+//! leads to the event getting deleted along with the end of
+//! the temporary's lifetime.
+template <EventClass EventT, typename BaseEventT>
+inline auto eventCast(event_ptr_tt<BaseEventT>&& eptr)
{
- return visitor(event);
+ return eptr && is<std::decay_t<EventT>>(*eptr)
+ ? event_ptr_tt<EventT>(static_cast<EventT*>(eptr.release()))
+ : nullptr;
}
namespace _impl {
- template <typename T, typename FnT>
- constexpr auto needs_downcast()
+ template <typename FnT, typename BaseT>
+ concept Invocable_With_Downcast = requires
{
- return !std::is_convertible_v<T, fn_arg_t<FnT>>;
- }
+ requires EventClass<BaseT>;
+ std::is_base_of_v<BaseT, std::remove_cvref_t<fn_arg_t<FnT>>>;
+ };
}
-// A single type-specific void visitor
-template <typename BaseEventT, typename FnT>
-inline std::enable_if_t<_impl::needs_downcast<BaseEventT, FnT>()
- && std::is_void_v<fn_return_t<FnT>>>
-visit(const BaseEventT& event, FnT&& visitor)
+template <EventClass BaseT, typename TailT>
+inline auto switchOnType(const BaseT& event, TailT&& tail)
{
- using event_type = fn_arg_t<FnT>;
- if (is<std::decay_t<event_type>>(event))
- visitor(static_cast<event_type>(event));
+ if constexpr (std::is_invocable_v<TailT, BaseT>) {
+ return tail(event);
+ } else if constexpr (_impl::Invocable_With_Downcast<TailT, BaseT>) {
+ using event_type = fn_arg_t<TailT>;
+ if (is<std::decay_t<event_type>>(event))
+ return tail(static_cast<event_type>(event));
+ return std::invoke_result_t<TailT, event_type>(); // Default-constructed
+ } else { // Treat it as a value to return
+ return std::forward<TailT>(tail);
+ }
}
-// A single type-specific non-void visitor with an optional default value
-// non-voidness is guarded by defaultValue type
-template <typename BaseEventT, typename FnT>
-inline std::enable_if_t<_impl::needs_downcast<BaseEventT, FnT>(), fn_return_t<FnT>>
-visit(const BaseEventT& event, FnT&& visitor,
- fn_return_t<FnT>&& defaultValue = {})
+template <EventClass BaseT, typename FnT1, typename... FnTs>
+inline auto switchOnType(const BaseT& event, FnT1&& fn1, FnTs&&... fns)
{
- using event_type = fn_arg_t<FnT>;
- if (is<std::decay_t<event_type>>(event))
- return visitor(static_cast<event_type>(event));
- return std::forward<fn_return_t<FnT>>(defaultValue);
+ using event_type1 = fn_arg_t<FnT1>;
+ if (is<std::decay_t<event_type1>>(event))
+ return fn1(static_cast<event_type1>(event));
+ return switchOnType(event, std::forward<FnTs>(fns)...);
}
-// A chain of 2 or more visitors
-template <typename BaseEventT, typename FnT1, typename FnT2, typename... FnTs>
-inline fn_return_t<FnT1> visit(const BaseEventT& event, FnT1&& visitor1,
- FnT2&& visitor2, FnTs&&... visitors)
+template <EventClass BaseT, typename... FnTs>
+[[deprecated("The new name for visit() is switchOnType()")]] //
+inline auto visit(const BaseT& event, FnTs&&... fns)
{
- using event_type1 = fn_arg_t<FnT1>;
- if (is<std::decay_t<event_type1>>(event))
- return visitor1(static_cast<event_type1&>(event));
- return visit(event, std::forward<FnT2>(visitor2),
- std::forward<FnTs>(visitors)...);
+ return switchOnType(event, std::forward<FnTs>(fns)...);
}
-// A facility overload that calls void-returning visit() on each event
+ // A facility overload that calls void-returning switchOnType() on each event
// over a range of event pointers
+// TODO: replace with ranges::for_each once all standard libraries have it
template <typename RangeT, typename... FnTs>
-inline auto visitEach(RangeT&& events, FnTs&&... visitors)
- -> std::enable_if_t<std::is_convertible_v<
- std::decay_t<decltype(**events.begin())>, Event>>
+inline auto visitEach(RangeT&& events, FnTs&&... fns)
+ requires std::is_void_v<
+ decltype(switchOnType(**begin(events), std::forward<FnTs>(fns)...))>
{
for (auto&& evtPtr: events)
- visit(*evtPtr, std::forward<FnTs>(visitors)...);
+ switchOnType(*evtPtr, std::forward<FnTs>(fns)...);
}
} // namespace Quotient
Q_DECLARE_METATYPE(Quotient::Event*)
diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp
index 802d8176..8db3b7e3 100644
--- a/lib/events/eventcontent.cpp
+++ b/lib/events/eventcontent.cpp
@@ -1,53 +1,55 @@
-/******************************************************************************
- * Copyright (C) 2017 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
- */
+// SPDX-FileCopyrightText: 2017 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "eventcontent.h"
#include "converters.h"
-#include "util.h"
+#include "logging.h"
#include <QtCore/QMimeDatabase>
+#include <QtCore/QFileInfo>
using namespace Quotient::EventContent;
+using std::move;
QJsonObject Base::toJson() const
{
QJsonObject o;
- fillJson(&o);
+ fillJson(o);
return o;
}
-FileInfo::FileInfo(const QUrl& u, qint64 payloadSize, const QMimeType& mimeType,
- const QString& originalFilename)
- : mimeType(mimeType)
- , url(u)
+FileInfo::FileInfo(const QFileInfo& fi)
+ : source(QUrl::fromLocalFile(fi.filePath())),
+ mimeType(QMimeDatabase().mimeTypeForFile(fi)),
+ payloadSize(fi.size()),
+ originalName(fi.fileName())
+{
+ Q_ASSERT(fi.isFile());
+}
+
+FileInfo::FileInfo(FileSourceInfo sourceInfo, qint64 payloadSize,
+ const QMimeType& mimeType, QString originalFilename)
+ : source(move(sourceInfo))
+ , mimeType(mimeType)
, payloadSize(payloadSize)
- , originalName(originalFilename)
-{}
+ , originalName(move(originalFilename))
+{
+ if (!isValid())
+ qCWarning(MESSAGES)
+ << "To client developers: using FileInfo(QUrl, qint64, ...) "
+ "constructor for non-mxc resources is deprecated since Quotient "
+ "0.7; for local resources, use FileInfo(QFileInfo) instead";
+}
-FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson,
- const QString& originalFilename)
- : originalInfoJson(infoJson)
+FileInfo::FileInfo(FileSourceInfo sourceInfo, const QJsonObject& infoJson,
+ QString originalFilename)
+ : source(move(sourceInfo))
+ , originalInfoJson(infoJson)
, mimeType(
QMimeDatabase().mimeTypeForName(infoJson["mimetype"_ls].toString()))
- , url(u)
, payloadSize(fromJson<qint64>(infoJson["size"_ls]))
- , originalName(originalFilename)
+ , originalName(move(originalFilename))
{
if (!mimeType.isValid())
mimeType = QMimeDatabase().mimeTypeForData(QByteArray());
@@ -55,49 +57,66 @@ FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson,
bool FileInfo::isValid() const
{
- return url.scheme() == "mxc"
- && (url.authority() + url.path()).count('/') == 1;
+ const auto& u = url();
+ return u.scheme() == "mxc" && (u.authority() + u.path()).count('/') == 1;
}
-void FileInfo::fillInfoJson(QJsonObject* infoJson) const
+QUrl FileInfo::url() const
{
- Q_ASSERT(infoJson);
- if (payloadSize != -1)
- infoJson->insert(QStringLiteral("size"), payloadSize);
- if (mimeType.isValid())
- infoJson->insert(QStringLiteral("mimetype"), mimeType.name());
+ return getUrlFromSourceInfo(source);
}
-ImageInfo::ImageInfo(const QUrl& u, qint64 fileSize, QMimeType mimeType,
- const QSize& imageSize, const QString& originalFilename)
- : FileInfo(u, fileSize, mimeType, originalFilename), imageSize(imageSize)
+QJsonObject Quotient::EventContent::toInfoJson(const FileInfo& info)
+{
+ QJsonObject infoJson;
+ if (info.payloadSize != -1)
+ infoJson.insert(QStringLiteral("size"), info.payloadSize);
+ if (info.mimeType.isValid())
+ infoJson.insert(QStringLiteral("mimetype"), info.mimeType.name());
+ return infoJson;
+}
+
+ImageInfo::ImageInfo(const QFileInfo& fi, QSize imageSize)
+ : FileInfo(fi), imageSize(imageSize)
+{}
+
+ImageInfo::ImageInfo(FileSourceInfo sourceInfo, qint64 fileSize,
+ const QMimeType& type, QSize imageSize,
+ const QString& originalFilename)
+ : FileInfo(move(sourceInfo), fileSize, type, originalFilename)
+ , imageSize(imageSize)
{}
-ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson,
+ImageInfo::ImageInfo(FileSourceInfo sourceInfo, const QJsonObject& infoJson,
const QString& originalFilename)
- : FileInfo(u, infoJson, originalFilename)
+ : FileInfo(move(sourceInfo), infoJson, originalFilename)
, imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt())
{}
-void ImageInfo::fillInfoJson(QJsonObject* infoJson) const
+QJsonObject Quotient::EventContent::toInfoJson(const ImageInfo& info)
{
- FileInfo::fillInfoJson(infoJson);
- if (imageSize.width() != -1)
- infoJson->insert(QStringLiteral("w"), imageSize.width());
- if (imageSize.height() != -1)
- infoJson->insert(QStringLiteral("h"), imageSize.height());
+ auto infoJson = toInfoJson(static_cast<const FileInfo&>(info));
+ if (info.imageSize.width() != -1)
+ infoJson.insert(QStringLiteral("w"), info.imageSize.width());
+ if (info.imageSize.height() != -1)
+ infoJson.insert(QStringLiteral("h"), info.imageSize.height());
+ return infoJson;
}
-Thumbnail::Thumbnail(const QJsonObject& infoJson)
- : ImageInfo(infoJson["thumbnail_url"_ls].toString(),
+Thumbnail::Thumbnail(const QJsonObject& infoJson,
+ const Omittable<EncryptedFileMetadata>& efm)
+ : ImageInfo(QUrl(infoJson["thumbnail_url"_ls].toString()),
infoJson["thumbnail_info"_ls].toObject())
-{}
+{
+ if (efm)
+ source = *efm;
+}
-void Thumbnail::fillInfoJson(QJsonObject* infoJson) const
+void Thumbnail::dumpTo(QJsonObject& infoJson) const
{
- if (url.isValid())
- infoJson->insert(QStringLiteral("thumbnail_url"), url.toString());
+ if (url().isValid())
+ fillJson(infoJson, { "thumbnail_url"_ls, "thumbnail_file"_ls }, source);
if (!imageSize.isEmpty())
- infoJson->insert(QStringLiteral("thumbnail_info"),
- toInfoJson<ImageInfo>(*this));
+ infoJson.insert(QStringLiteral("thumbnail_info"),
+ toInfoJson(*this));
}
diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h
index 0d4c047e..af26c0a4 100644
--- a/lib/events/eventcontent.h
+++ b/lib/events/eventcontent.h
@@ -1,282 +1,254 @@
-/******************************************************************************
- * Copyright (C) 2017 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
- */
+// SPDX-FileCopyrightText: 2017 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
// This file contains generic event content definitions, applicable to room
// message events as well as other events (e.g., avatars).
+#include "filesourceinfo.h"
+#include "quotient_export.h"
+
#include <QtCore/QJsonObject>
+#include <QtCore/QMetaType>
#include <QtCore/QMimeType>
#include <QtCore/QSize>
#include <QtCore/QUrl>
-#include <QtCore/QMetaType>
-namespace Quotient {
-namespace EventContent {
- /**
- * A base class for all content types that can be stored
- * in a RoomMessageEvent
- *
- * Each content type class should have a constructor taking
- * a QJsonObject and override fillJson() with an implementation
- * that will fill the target QJsonObject with stored values. It is
- * assumed but not required that a content object can also be created
- * from plain data.
- */
- class Base {
- public:
- explicit Base(QJsonObject o = {}) : originalJson(std::move(o)) {}
- virtual ~Base() = default;
-
- // FIXME: make toJson() from converters.* work on base classes
- QJsonObject toJson() const;
-
- public:
- QJsonObject originalJson;
-
- protected:
- Base(const Base&) = default;
- Base(Base&&) = default;
-
- virtual void fillJson(QJsonObject* o) const = 0;
- };
-
- // The below structures fairly follow CS spec 11.2.1.6. The overall
- // set of attributes for each content types is a superset of the spec
- // but specific aggregation structure is altered. See doc comments to
- // each type for the list of available attributes.
-
- // A quick classes inheritance structure follows:
- // FileInfo
- // FileContent : UrlBasedContent<FileInfo, Thumbnail>
- // AudioContent : UrlBasedContent<FileInfo, Duration>
- // ImageInfo : FileInfo + imageSize attribute
- // ImageContent : UrlBasedContent<ImageInfo, Thumbnail>
- // VideoContent : UrlBasedContent<ImageInfo, Thumbnail, Duration>
-
- /**
- * A base/mixin class for structures representing an "info" object for
- * some content types. These include most attachment types currently in
- * the CS API spec.
- *
- * In order to use it in a content class, derive both from TypedBase
- * (or Base) and from FileInfo (or its derivative, such as \p ImageInfo)
- * and call fillInfoJson() to fill the "info" subobject. Make sure
- * to pass an "info" part of JSON to FileInfo constructor, not the whole
- * JSON content, as well as contents of "url" (or a similar key) and
- * optionally "filename" node from the main JSON content. Assuming you
- * don't do unusual things, you should use \p UrlBasedContent<> instead
- * of doing multiple inheritance and overriding Base::fillJson() by hand.
- *
- * This class is not polymorphic.
- */
- class FileInfo {
- public:
- explicit FileInfo(const QUrl& u, qint64 payloadSize = -1,
- const QMimeType& mimeType = {},
- const QString& originalFilename = {});
- FileInfo(const QUrl& u, const QJsonObject& infoJson,
- const QString& originalFilename = {});
-
- bool isValid() const;
-
- void fillInfoJson(QJsonObject* infoJson) const;
-
- /**
- * \brief Extract media id from the URL
- *
- * This can be used, e.g., to construct a QML-facing image://
- * URI as follows:
- * \code "image://provider/" + info.mediaId() \endcode
- */
- QString mediaId() const { return url.authority() + url.path(); }
-
- public:
- QJsonObject originalInfoJson;
- QMimeType mimeType;
- QUrl url;
- qint64 payloadSize;
- QString originalName;
- };
-
- template <typename InfoT>
- QJsonObject toInfoJson(const InfoT& info)
+class QFileInfo;
+
+namespace Quotient::EventContent {
+//! \brief Base for all content types that can be stored in RoomMessageEvent
+//!
+//! Each content type class should have a constructor taking
+//! a QJsonObject and override fillJson() with an implementation
+//! that will fill the target QJsonObject with stored values. It is
+//! assumed but not required that a content object can also be created
+//! from plain data.
+class QUOTIENT_API Base {
+public:
+ explicit Base(QJsonObject o = {}) : originalJson(std::move(o)) {}
+ virtual ~Base() = default;
+
+ QJsonObject toJson() const;
+
+public:
+ QJsonObject originalJson;
+
+ // You can't assign those classes
+ Base& operator=(const Base&) = delete;
+ Base& operator=(Base&&) = delete;
+
+protected:
+ Base(const Base&) = default;
+ Base(Base&&) noexcept = default;
+
+ virtual void fillJson(QJsonObject&) const = 0;
+};
+
+// The below structures fairly follow CS spec 11.2.1.6. The overall
+// set of attributes for each content types is a superset of the spec
+// but specific aggregation structure is altered. See doc comments to
+// each type for the list of available attributes.
+
+// A quick classes inheritance structure follows (the definitions are
+// spread across eventcontent.h and roommessageevent.h):
+// UrlBasedContent<InfoT> : InfoT + thumbnail data
+// PlayableContent<InfoT> : + duration attribute
+// FileInfo
+// FileContent = UrlBasedContent<FileInfo>
+// AudioContent = PlayableContent<FileInfo>
+// ImageInfo : FileInfo + imageSize attribute
+// ImageContent = UrlBasedContent<ImageInfo>
+// VideoContent = PlayableContent<ImageInfo>
+
+//! \brief Mix-in class representing `info` subobject in content JSON
+//!
+//! This is one of base classes for content types that deal with files or
+//! URLs. It stores the file metadata attributes, such as size, MIME type
+//! etc. found in the `content/info` subobject of event JSON payloads.
+//! Actual content classes derive from this class _and_ TypedBase that
+//! provides a polymorphic interface to access data in the mix-in. FileInfo
+//! (as well as ImageInfo, that adds image size to the metadata) is NOT
+//! polymorphic and is used in a non-polymorphic way to store thumbnail
+//! metadata (in a separate instance), next to the metadata on the file
+//! itself.
+//!
+//! If you need to make a new _content_ (not info) class based on files/URLs
+//! take UrlBasedContent as the example, i.e.:
+//! 1. Double-inherit from this class (or ImageInfo) and TypedBase.
+//! 2. Provide a constructor from QJsonObject that will pass the `info`
+//! subobject (not the whole content JSON) down to FileInfo/ImageInfo.
+//! 3. Override fillJson() to customise the JSON export logic. Make sure
+//! to call toInfoJson() from it to produce the payload for the `info`
+//! subobject in the JSON payload.
+//!
+//! \sa ImageInfo, FileContent, ImageContent, AudioContent, VideoContent,
+//! UrlBasedContent
+class QUOTIENT_API FileInfo {
+public:
+ FileInfo() = default;
+ //! \brief Construct from a QFileInfo object
+ //!
+ //! \param fi a QFileInfo object referring to an existing file
+ explicit FileInfo(const QFileInfo& fi);
+ explicit FileInfo(FileSourceInfo sourceInfo, qint64 payloadSize = -1,
+ const QMimeType& mimeType = {},
+ QString originalFilename = {});
+ //! \brief Construct from a JSON `info` payload
+ //!
+ //! Make sure to pass the `info` subobject of content JSON, not the
+ //! whole JSON content.
+ FileInfo(FileSourceInfo sourceInfo, const QJsonObject& infoJson,
+ QString originalFilename = {});
+
+ bool isValid() const;
+ QUrl url() const;
+
+ //! \brief Extract media id from the URL
+ //!
+ //! This can be used, e.g., to construct a QML-facing image://
+ //! URI as follows:
+ //! \code "image://provider/" + info.mediaId() \endcode
+ QString mediaId() const { return url().authority() + url().path(); }
+
+public:
+ FileSourceInfo source;
+ QJsonObject originalInfoJson;
+ QMimeType mimeType;
+ qint64 payloadSize = 0;
+ QString originalName;
+};
+
+QUOTIENT_API QJsonObject toInfoJson(const FileInfo& info);
+
+//! \brief A content info class for image/video content types and thumbnails
+class QUOTIENT_API ImageInfo : public FileInfo {
+public:
+ ImageInfo() = default;
+ explicit ImageInfo(const QFileInfo& fi, QSize imageSize = {});
+ explicit ImageInfo(FileSourceInfo sourceInfo, qint64 fileSize = -1,
+ const QMimeType& type = {}, QSize imageSize = {},
+ const QString& originalFilename = {});
+ ImageInfo(FileSourceInfo sourceInfo, const QJsonObject& infoJson,
+ const QString& originalFilename = {});
+
+public:
+ QSize imageSize;
+};
+
+QUOTIENT_API QJsonObject toInfoJson(const ImageInfo& info);
+
+//! \brief An auxiliary class for an info type that carries a thumbnail
+//!
+//! This class saves/loads a thumbnail to/from `info` subobject of
+//! the JSON representation of event content; namely, `info/thumbnail_url`
+//! (or, in case of an encrypted thumbnail, `info/thumbnail_file`) and
+//! `info/thumbnail_info` fields are used.
+class QUOTIENT_API Thumbnail : public ImageInfo {
+public:
+ using ImageInfo::ImageInfo;
+ explicit Thumbnail(const QJsonObject& infoJson,
+ const Omittable<EncryptedFileMetadata>& efm = none);
+
+ //! \brief Add thumbnail information to the passed `info` JSON object
+ void dumpTo(QJsonObject& infoJson) const;
+};
+
+class QUOTIENT_API TypedBase : public Base {
+public:
+ virtual QMimeType type() const = 0;
+ virtual const FileInfo* fileInfo() const { return nullptr; }
+ virtual FileInfo* fileInfo() { return nullptr; }
+ virtual const Thumbnail* thumbnailInfo() const { return nullptr; }
+
+protected:
+ explicit TypedBase(QJsonObject o = {}) : Base(std::move(o)) {}
+ using Base::Base;
+};
+
+//! \brief A template class for content types with a URL and additional info
+//!
+//! Types that derive from this class template take `url` (or, if the file
+//! is encrypted, `file`) and, optionally, `filename` values from
+//! the top-level JSON object and the rest of information from the `info`
+//! subobject, as defined by the parameter type.
+//! \tparam InfoT base info class - FileInfo or ImageInfo
+template <class InfoT>
+class UrlBasedContent : public TypedBase, public InfoT {
+public:
+ using InfoT::InfoT;
+ explicit UrlBasedContent(const QJsonObject& json)
+ : TypedBase(json)
+ , InfoT(QUrl(json["url"].toString()), json["info"].toObject(),
+ json["filename"].toString())
+ , thumbnail(FileInfo::originalInfoJson)
{
- QJsonObject infoJson;
- info.fillInfoJson(&infoJson);
- return infoJson;
+ if (const auto efmJson = json.value("file"_ls).toObject();
+ !efmJson.isEmpty())
+ InfoT::source = fromJson<EncryptedFileMetadata>(efmJson);
+ // Two small hacks on originalJson to expose mediaIds to QML
+ originalJson.insert("mediaId", InfoT::mediaId());
+ originalJson.insert("thumbnailMediaId", thumbnail.mediaId());
}
- /**
- * A content info class for image content types: image, thumbnail, video
- */
- class ImageInfo : public FileInfo {
- public:
- explicit ImageInfo(const QUrl& u, qint64 fileSize = -1,
- QMimeType mimeType = {}, const QSize& imageSize = {},
- const QString& originalFilename = {});
- ImageInfo(const QUrl& u, const QJsonObject& infoJson,
- const QString& originalFilename = {});
-
- void fillInfoJson(QJsonObject* infoJson) const;
-
- public:
- QSize imageSize;
- };
-
- /**
- * An auxiliary class for an info type that carries a thumbnail
- *
- * This class saves/loads a thumbnail to/from "info" subobject of
- * the JSON representation of event content; namely,
- * "info/thumbnail_url" and "info/thumbnail_info" fields are used.
- */
- class Thumbnail : public ImageInfo {
- public:
- Thumbnail() : ImageInfo(QUrl()) {} // To allow empty thumbnails
- Thumbnail(const QJsonObject& infoJson);
- Thumbnail(const ImageInfo& info) : ImageInfo(info) {}
- using ImageInfo::ImageInfo;
-
- /**
- * Writes thumbnail information to "thumbnail_info" subobject
- * and thumbnail URL to "thumbnail_url" node inside "info".
- */
- void fillInfoJson(QJsonObject* infoJson) const;
- };
-
- class TypedBase : public Base {
- public:
- explicit TypedBase(QJsonObject o = {}) : Base(std::move(o)) {}
- virtual QMimeType type() const = 0;
- virtual const FileInfo* fileInfo() const { return nullptr; }
- virtual FileInfo* fileInfo() { return nullptr; }
- virtual const Thumbnail* thumbnailInfo() const { return nullptr; }
-
- protected:
- using Base::Base;
- };
-
- /**
- * A base class for content types that have a URL and additional info
- *
- * Types that derive from this class template take "url" and,
- * optionally, "filename" values from the top-level JSON object and
- * the rest of information from the "info" subobject, as defined by
- * the parameter type.
- *
- * \tparam InfoT base info class
- */
- template <class InfoT>
- class UrlBasedContent : public TypedBase, public InfoT {
- public:
- using InfoT::InfoT;
- explicit UrlBasedContent(const QJsonObject& json)
- : TypedBase(json)
- , InfoT(json["url"].toString(), json["info"].toObject(),
- json["filename"].toString())
- {
- // A small hack to facilitate links creation in QML.
- originalJson.insert("mediaId", InfoT::mediaId());
- }
-
- QMimeType type() const override { return InfoT::mimeType; }
- const FileInfo* fileInfo() const override { return this; }
- FileInfo* fileInfo() override { return this; }
-
- protected:
- void fillJson(QJsonObject* json) const override
- {
- Q_ASSERT(json);
- json->insert("url", InfoT::url.toString());
- if (!InfoT::originalName.isEmpty())
- json->insert("filename", InfoT::originalName);
- json->insert("info", toInfoJson<InfoT>(*this));
- }
- };
-
- template <typename InfoT>
- class UrlWithThumbnailContent : public UrlBasedContent<InfoT> {
- public:
- // NB: when using inherited constructors, thumbnail has to be
- // initialised separately
- using UrlBasedContent<InfoT>::UrlBasedContent;
- explicit UrlWithThumbnailContent(const QJsonObject& json)
- : UrlBasedContent<InfoT>(json), thumbnail(InfoT::originalInfoJson)
- {
- // Another small hack, to simplify making a thumbnail link
- UrlBasedContent<InfoT>::originalJson.insert("thumbnailMediaId",
- thumbnail.mediaId());
- }
-
- const Thumbnail* thumbnailInfo() const override { return &thumbnail; }
-
- public:
- Thumbnail thumbnail;
-
- protected:
- void fillJson(QJsonObject* json) const override
- {
- UrlBasedContent<InfoT>::fillJson(json);
- auto infoJson = json->take("info").toObject();
- thumbnail.fillInfoJson(&infoJson);
- json->insert("info", infoJson);
- }
- };
-
- /**
- * Content class for m.image
- *
- * Available fields:
- * - corresponding to the top-level JSON:
- * - url
- * - filename (extension to the spec)
- * - corresponding to the "info" subobject:
- * - payloadSize ("size" in JSON)
- * - mimeType ("mimetype" in JSON)
- * - imageSize (QSize for a combination of "h" and "w" in JSON)
- * - thumbnail.url ("thumbnail_url" in JSON)
- * - corresponding to the "info/thumbnail_info" subobject: contents of
- * thumbnail field, in the same vein as for the main image:
- * - payloadSize
- * - mimeType
- * - imageSize
- */
- using ImageContent = UrlWithThumbnailContent<ImageInfo>;
-
- /**
- * Content class for m.file
- *
- * Available fields:
- * - corresponding to the top-level JSON:
- * - url
- * - filename
- * - corresponding to the "info" subobject:
- * - payloadSize ("size" in JSON)
- * - mimeType ("mimetype" in JSON)
- * - thumbnail.url ("thumbnail_url" in JSON)
- * - corresponding to the "info/thumbnail_info" subobject:
- * - thumbnail.payloadSize
- * - thumbnail.mimeType
- * - thumbnail.imageSize (QSize for "h" and "w" in JSON)
- */
- using FileContent = UrlWithThumbnailContent<FileInfo>;
-} // namespace EventContent
-} // namespace Quotient
+ QMimeType type() const override { return InfoT::mimeType; }
+ const FileInfo* fileInfo() const override { return this; }
+ FileInfo* fileInfo() override { return this; }
+ const Thumbnail* thumbnailInfo() const override { return &thumbnail; }
+
+public:
+ Thumbnail thumbnail;
+
+protected:
+ virtual void fillInfoJson(QJsonObject& infoJson [[maybe_unused]]) const
+ {}
+
+ void fillJson(QJsonObject& json) const override
+ {
+ Quotient::fillJson(json, { "url"_ls, "file"_ls }, InfoT::source);
+ if (!InfoT::originalName.isEmpty())
+ json.insert("filename", InfoT::originalName);
+ auto infoJson = toInfoJson(*this);
+ if (thumbnail.isValid())
+ thumbnail.dumpTo(infoJson);
+ fillInfoJson(infoJson);
+ json.insert("info", infoJson);
+ }
+};
+
+//! \brief Content class for m.image
+//!
+//! Available fields:
+//! - corresponding to the top-level JSON:
+//! - source (corresponding to `url` or `file` in JSON)
+//! - filename (extension to the spec)
+//! - corresponding to the `info` subobject:
+//! - payloadSize (`size` in JSON)
+//! - mimeType (`mimetype` in JSON)
+//! - imageSize (QSize for a combination of `h` and `w` in JSON)
+//! - thumbnail.url (`thumbnail_url` in JSON)
+//! - corresponding to the `info/thumbnail_info` subobject: contents of
+//! thumbnail field, in the same vein as for the main image:
+//! - payloadSize
+//! - mimeType
+//! - imageSize
+using ImageContent = UrlBasedContent<ImageInfo>;
+
+//! \brief Content class for m.file
+//!
+//! Available fields:
+//! - corresponding to the top-level JSON:
+//! - source (corresponding to `url` or `file` in JSON)
+//! - filename
+//! - corresponding to the `info` subobject:
+//! - payloadSize (`size` in JSON)
+//! - mimeType (`mimetype` in JSON)
+//! - thumbnail.source (`thumbnail_url` or `thumbnail_file` in JSON)
+//! - corresponding to the `info/thumbnail_info` subobject:
+//! - thumbnail.payloadSize
+//! - thumbnail.mimeType
+//! - thumbnail.imageSize (QSize for `h` and `w` in JSON)
+using FileContent = UrlBasedContent<FileInfo>;
+} // namespace Quotient::EventContent
Q_DECLARE_METATYPE(const Quotient::EventContent::TypedBase*)
diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h
index ebb96441..b4ac154c 100644
--- a/lib/events/eventloader.h
+++ b/lib/events/eventloader.h
@@ -1,86 +1,13 @@
-/******************************************************************************
- * Copyright (C) 2018 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
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "stateevent.h"
namespace Quotient {
-namespace _impl {
- template <typename BaseEventT>
- static inline auto loadEvent(const QJsonObject& json,
- const QString& matrixType)
- {
- if (auto e = EventFactory<BaseEventT>::make(json, matrixType))
- return e;
- return makeEvent<BaseEventT>(unknownEventTypeId(), json);
- }
-} // namespace _impl
-
-/*! Create an event with proper type from a JSON object
- *
- * Use this factory template to detect the type from the JSON object
- * contents (the detected event type should derive from the template
- * parameter type) and create an event object of that type.
- */
-template <typename BaseEventT>
-inline event_ptr_tt<BaseEventT> loadEvent(const QJsonObject& fullJson)
-{
- return _impl::loadEvent<BaseEventT>(fullJson, fullJson[TypeKeyL].toString());
-}
-
-/*! Create an event from a type string and content JSON
- *
- * Use this factory template to resolve the C++ type from the Matrix
- * type string in \p matrixType and create an event of that type that has
- * its content part set to \p content.
- */
-template <typename BaseEventT>
-inline event_ptr_tt<BaseEventT> loadEvent(const QString& matrixType,
- const QJsonObject& content)
-{
- return _impl::loadEvent<BaseEventT>(basicEventJson(matrixType, content),
- matrixType);
+struct [[deprecated(
+ "This header is obsolete since libQuotient 0.7; include a header with"
+ " the respective event type definition instead")]] EventLoaderH;
+StateEventPtr eventLoaderH(EventLoaderH&);
}
-
-/*! Create a state event from a type string, content JSON and state key
- *
- * Use this factory to resolve the C++ type from the Matrix type string
- * in \p matrixType and create a state event of that type with content part
- * set to \p content and state key set to \p stateKey (empty by default).
- */
-inline StateEventPtr loadStateEvent(const QString& matrixType,
- const QJsonObject& content,
- const QString& stateKey = {})
-{
- return _impl::loadEvent<StateEventBase>(
- basicStateEventJson(matrixType, content, stateKey), matrixType);
-}
-
-template <typename EventT>
-struct JsonConverter<event_ptr_tt<EventT>> {
- static auto load(const QJsonValue& jv)
- {
- return loadEvent<EventT>(jv.toObject());
- }
- static auto load(const QJsonDocument& jd)
- {
- return loadEvent<EventT>(jd.object());
- }
-};
-} // namespace Quotient
diff --git a/lib/events/eventrelation.cpp b/lib/events/eventrelation.cpp
new file mode 100644
index 00000000..04972f45
--- /dev/null
+++ b/lib/events/eventrelation.cpp
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: 2022 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "eventrelation.h"
+
+#include "../logging.h"
+#include "event.h"
+
+using namespace Quotient;
+
+void JsonObjectConverter<EventRelation>::dumpTo(QJsonObject& jo,
+ const EventRelation& pod)
+{
+ if (pod.type.isEmpty()) {
+ qCWarning(MAIN) << "Empty relation type; won't dump to JSON";
+ return;
+ }
+ jo.insert(RelTypeKey, pod.type);
+ jo.insert(EventIdKey, pod.eventId);
+ if (pod.type == EventRelation::AnnotationType)
+ jo.insert(QStringLiteral("key"), pod.key);
+}
+
+void JsonObjectConverter<EventRelation>::fillFrom(const QJsonObject& jo,
+ EventRelation& pod)
+{
+ if (const auto replyJson = jo.value(EventRelation::ReplyType).toObject();
+ !replyJson.isEmpty()) {
+ pod.type = EventRelation::ReplyType;
+ fromJson(replyJson[EventIdKeyL], pod.eventId);
+ } else {
+ // The experimental logic for generic relationships (MSC1849)
+ fromJson(jo[RelTypeKey], pod.type);
+ fromJson(jo[EventIdKeyL], pod.eventId);
+ if (pod.type == EventRelation::AnnotationType)
+ fromJson(jo["key"_ls], pod.key);
+ }
+}
diff --git a/lib/events/eventrelation.h b/lib/events/eventrelation.h
new file mode 100644
index 00000000..2a841cf1
--- /dev/null
+++ b/lib/events/eventrelation.h
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2022 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+
+#include "converters.h"
+
+namespace Quotient {
+
+[[maybe_unused]] constexpr auto RelatesToKey = "m.relates_to"_ls;
+constexpr auto RelTypeKey = "rel_type"_ls;
+
+struct QUOTIENT_API EventRelation {
+ using reltypeid_t = QLatin1String;
+
+ QString type;
+ QString eventId;
+ QString key = {}; // Only used for m.annotation for now
+
+ static constexpr auto ReplyType = "m.in_reply_to"_ls;
+ static constexpr auto AnnotationType = "m.annotation"_ls;
+ static constexpr auto ReplacementType = "m.replace"_ls;
+
+ static EventRelation replyTo(QString eventId)
+ {
+ return { ReplyType, std::move(eventId) };
+ }
+ static EventRelation annotate(QString eventId, QString key)
+ {
+ return { AnnotationType, std::move(eventId), std::move(key) };
+ }
+ static EventRelation replace(QString eventId)
+ {
+ return { ReplacementType, std::move(eventId) };
+ }
+
+ [[deprecated("Use ReplyType variable instead")]]
+ static constexpr auto Reply() { return ReplyType; }
+ [[deprecated("Use AnnotationType variable instead")]] //
+ static constexpr auto Annotation() { return AnnotationType; }
+ [[deprecated("Use ReplacementType variable instead")]] //
+ static constexpr auto Replacement() { return ReplacementType; }
+};
+
+template <>
+struct QUOTIENT_API JsonObjectConverter<EventRelation> {
+ static void dumpTo(QJsonObject& jo, const EventRelation& pod);
+ static void fillFrom(const QJsonObject& jo, EventRelation& pod);
+};
+
+}
+
diff --git a/lib/events/filesourceinfo.cpp b/lib/events/filesourceinfo.cpp
new file mode 100644
index 00000000..a60d86d2
--- /dev/null
+++ b/lib/events/filesourceinfo.cpp
@@ -0,0 +1,163 @@
+// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "filesourceinfo.h"
+
+#include "logging.h"
+#include "util.h"
+
+#ifdef Quotient_E2EE_ENABLED
+# include "e2ee/qolmutils.h"
+
+# include <QtCore/QCryptographicHash>
+
+# include <openssl/evp.h>
+#endif
+
+using namespace Quotient;
+
+QByteArray Quotient::decryptFile(const QByteArray& ciphertext,
+ const EncryptedFileMetadata& metadata)
+{
+#ifdef Quotient_E2EE_ENABLED
+ if (QByteArray::fromBase64(metadata.hashes["sha256"_ls].toLatin1())
+ != QCryptographicHash::hash(ciphertext, QCryptographicHash::Sha256)) {
+ qCWarning(E2EE) << "Hash verification failed for file";
+ return {};
+ }
+
+ auto _key = metadata.key.k;
+ const auto keyBytes = QByteArray::fromBase64(
+ _key.replace(u'_', u'/').replace(u'-', u'+').toLatin1());
+ int length;
+ auto* ctx = EVP_CIPHER_CTX_new();
+ QByteArray plaintext(ciphertext.size() + EVP_MAX_BLOCK_LENGTH - 1, '\0');
+ EVP_DecryptInit_ex(
+ ctx, EVP_aes_256_ctr(), nullptr,
+ reinterpret_cast<const unsigned char*>(keyBytes.data()),
+ reinterpret_cast<const unsigned char*>(
+ QByteArray::fromBase64(metadata.iv.toLatin1()).data()));
+ EVP_DecryptUpdate(ctx, reinterpret_cast<unsigned char*>(plaintext.data()),
+ &length,
+ reinterpret_cast<const unsigned char*>(ciphertext.data()),
+ ciphertext.size());
+ EVP_DecryptFinal_ex(ctx,
+ reinterpret_cast<unsigned char*>(plaintext.data())
+ + length,
+ &length);
+ EVP_CIPHER_CTX_free(ctx);
+ return plaintext.left(ciphertext.size());
+#else
+ qWarning(MAIN) << "This build of libQuotient doesn't support E2EE, "
+ "cannot decrypt the file";
+ return ciphertext;
+#endif
+}
+
+std::pair<EncryptedFileMetadata, QByteArray> Quotient::encryptFile(
+ const QByteArray& plainText)
+{
+#ifdef Quotient_E2EE_ENABLED
+ auto k = RandomBuffer(32);
+ auto kBase64 = k.toBase64(QByteArray::Base64UrlEncoding
+ | QByteArray::OmitTrailingEquals);
+ auto iv = RandomBuffer(16);
+ JWK key = {
+ "oct"_ls, { "encrypt"_ls, "decrypt"_ls }, "A256CTR"_ls, kBase64, true
+ };
+
+ int length = -1;
+ auto* ctx = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr, k.bytes(), iv.bytes());
+ const auto blockSize = EVP_CIPHER_CTX_block_size(ctx);
+ QByteArray cipherText(plainText.size() + blockSize - 1, '\0');
+ EVP_EncryptUpdate(ctx, reinterpret_cast<unsigned char*>(cipherText.data()),
+ &length,
+ reinterpret_cast<const unsigned char*>(plainText.data()),
+ plainText.size());
+ EVP_EncryptFinal_ex(ctx,
+ reinterpret_cast<unsigned char*>(cipherText.data())
+ + length,
+ &length);
+ EVP_CIPHER_CTX_free(ctx);
+
+ auto hash = QCryptographicHash::hash(cipherText, QCryptographicHash::Sha256)
+ .toBase64(QByteArray::OmitTrailingEquals);
+ auto ivBase64 = iv.toBase64(QByteArray::OmitTrailingEquals);
+ EncryptedFileMetadata efm = {
+ {}, key, ivBase64, { { QStringLiteral("sha256"), hash } }, "v2"_ls
+ };
+ return { efm, cipherText };
+#else
+ return {};
+#endif
+}
+
+void JsonObjectConverter<EncryptedFileMetadata>::dumpTo(QJsonObject& jo,
+ const EncryptedFileMetadata& pod)
+{
+ addParam<>(jo, QStringLiteral("url"), pod.url);
+ addParam<>(jo, QStringLiteral("key"), pod.key);
+ addParam<>(jo, QStringLiteral("iv"), pod.iv);
+ addParam<>(jo, QStringLiteral("hashes"), pod.hashes);
+ addParam<>(jo, QStringLiteral("v"), pod.v);
+}
+
+void JsonObjectConverter<EncryptedFileMetadata>::fillFrom(const QJsonObject& jo,
+ EncryptedFileMetadata& pod)
+{
+ fromJson(jo.value("url"_ls), pod.url);
+ fromJson(jo.value("key"_ls), pod.key);
+ fromJson(jo.value("iv"_ls), pod.iv);
+ fromJson(jo.value("hashes"_ls), pod.hashes);
+ fromJson(jo.value("v"_ls), pod.v);
+}
+
+void JsonObjectConverter<JWK>::dumpTo(QJsonObject& jo, const JWK& pod)
+{
+ addParam<>(jo, QStringLiteral("kty"), pod.kty);
+ addParam<>(jo, QStringLiteral("key_ops"), pod.keyOps);
+ addParam<>(jo, QStringLiteral("alg"), pod.alg);
+ addParam<>(jo, QStringLiteral("k"), pod.k);
+ addParam<>(jo, QStringLiteral("ext"), pod.ext);
+}
+
+void JsonObjectConverter<JWK>::fillFrom(const QJsonObject& jo, JWK& pod)
+{
+ fromJson(jo.value("kty"_ls), pod.kty);
+ fromJson(jo.value("key_ops"_ls), pod.keyOps);
+ fromJson(jo.value("alg"_ls), pod.alg);
+ fromJson(jo.value("k"_ls), pod.k);
+ fromJson(jo.value("ext"_ls), pod.ext);
+}
+
+QUrl Quotient::getUrlFromSourceInfo(const FileSourceInfo& fsi)
+{
+ return std::visit(Overloads { [](const QUrl& url) { return url; },
+ [](const EncryptedFileMetadata& efm) {
+ return efm.url;
+ } },
+ fsi);
+}
+
+void Quotient::setUrlInSourceInfo(FileSourceInfo& fsi, const QUrl& newUrl)
+{
+ std::visit(Overloads { [&newUrl](QUrl& url) { url = newUrl; },
+ [&newUrl](EncryptedFileMetadata& efm) {
+ efm.url = newUrl;
+ } },
+ fsi);
+}
+
+void Quotient::fillJson(QJsonObject& jo,
+ const std::array<QLatin1String, 2>& jsonKeys,
+ const FileSourceInfo& fsi)
+{
+ // NB: Keeping variant_size_v out of the function signature for readability.
+ // NB2: Can't use jsonKeys directly inside static_assert as its value is
+ // unknown so the compiler cannot ensure size() is constexpr (go figure...)
+ static_assert(
+ std::variant_size_v<FileSourceInfo> == decltype(jsonKeys) {}.size());
+ jo.insert(jsonKeys[fsi.index()], toJson(fsi));
+}
diff --git a/lib/events/filesourceinfo.h b/lib/events/filesourceinfo.h
new file mode 100644
index 00000000..8f7e3cbe
--- /dev/null
+++ b/lib/events/filesourceinfo.h
@@ -0,0 +1,90 @@
+// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+
+#include "converters.h"
+
+#include <array>
+
+namespace Quotient {
+/**
+ * JSON Web Key object as specified in
+ * https://spec.matrix.org/unstable/client-server-api/#extensions-to-mroommessage-msgtypes
+ * The only currently relevant member is `k`, the rest needs to be set to the defaults specified in the spec.
+ */
+struct JWK
+{
+ Q_GADGET
+ Q_PROPERTY(QString kty MEMBER kty CONSTANT)
+ Q_PROPERTY(QStringList keyOps MEMBER keyOps CONSTANT)
+ Q_PROPERTY(QString alg MEMBER alg CONSTANT)
+ Q_PROPERTY(QString k MEMBER k CONSTANT)
+ Q_PROPERTY(bool ext MEMBER ext CONSTANT)
+
+public:
+ QString kty;
+ QStringList keyOps;
+ QString alg;
+ QString k;
+ bool ext;
+};
+
+struct QUOTIENT_API EncryptedFileMetadata {
+ Q_GADGET
+ Q_PROPERTY(QUrl url MEMBER url CONSTANT)
+ Q_PROPERTY(JWK key MEMBER key CONSTANT)
+ Q_PROPERTY(QString iv MEMBER iv CONSTANT)
+ Q_PROPERTY(QHash<QString, QString> hashes MEMBER hashes CONSTANT)
+ Q_PROPERTY(QString v MEMBER v CONSTANT)
+
+public:
+ QUrl url;
+ JWK key;
+ QString iv;
+ QHash<QString, QString> hashes;
+ QString v;
+};
+
+QUOTIENT_API std::pair<EncryptedFileMetadata, QByteArray> encryptFile(
+ const QByteArray& plainText);
+QUOTIENT_API QByteArray decryptFile(const QByteArray& ciphertext,
+ const EncryptedFileMetadata& metadata);
+
+template <>
+struct QUOTIENT_API JsonObjectConverter<EncryptedFileMetadata> {
+ static void dumpTo(QJsonObject& jo, const EncryptedFileMetadata& pod);
+ static void fillFrom(const QJsonObject& jo, EncryptedFileMetadata& pod);
+};
+
+template <>
+struct QUOTIENT_API JsonObjectConverter<JWK> {
+ static void dumpTo(QJsonObject& jo, const JWK& pod);
+ static void fillFrom(const QJsonObject& jo, JWK& pod);
+};
+
+using FileSourceInfo = std::variant<QUrl, EncryptedFileMetadata>;
+
+QUOTIENT_API QUrl getUrlFromSourceInfo(const FileSourceInfo& fsi);
+
+QUOTIENT_API void setUrlInSourceInfo(FileSourceInfo& fsi, const QUrl& newUrl);
+
+// The way FileSourceInfo is stored in JSON requires an extra parameter so
+// the original template is not applicable
+template <>
+void fillJson(QJsonObject&, const FileSourceInfo&) = delete;
+
+//! \brief Export FileSourceInfo to a JSON object
+//!
+//! Depending on what is stored inside FileSourceInfo, this function will insert
+//! - a key-to-string pair where key is taken from jsonKeys[0] and the string
+//! is the URL, if FileSourceInfo stores a QUrl;
+//! - a key-to-object mapping where key is taken from jsonKeys[1] and the object
+//! is the result of converting EncryptedFileMetadata to JSON,
+//! if FileSourceInfo stores EncryptedFileMetadata
+QUOTIENT_API void fillJson(QJsonObject& jo,
+ const std::array<QLatin1String, 2>& jsonKeys,
+ const FileSourceInfo& fsi);
+
+} // namespace Quotient
diff --git a/lib/events/keyverificationevent.h b/lib/events/keyverificationevent.h
new file mode 100644
index 00000000..80aebcf3
--- /dev/null
+++ b/lib/events/keyverificationevent.h
@@ -0,0 +1,258 @@
+// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+
+#include "event.h"
+
+namespace Quotient {
+
+static constexpr auto SasV1Method = "m.sas.v1"_ls;
+
+class QUOTIENT_API KeyVerificationEvent : public Event {
+public:
+ QUO_BASE_EVENT(KeyVerificationEvent, "m.key.*"_ls, Event::BaseMetaType)
+
+ using Event::Event;
+
+ /// An opaque identifier for the verification request. Must
+ /// be unique with respect to the devices involved.
+ QUO_CONTENT_GETTER(QString, transactionId)
+};
+
+/// Requests a key verification with another user's devices.
+/// Typically sent as a to-device event.
+class QUOTIENT_API KeyVerificationRequestEvent : public KeyVerificationEvent {
+public:
+ QUO_EVENT(KeyVerificationRequestEvent, "m.key.verification.request")
+
+ using KeyVerificationEvent::KeyVerificationEvent;
+ KeyVerificationRequestEvent(const QString& transactionId,
+ const QString& fromDevice,
+ const QStringList& methods,
+ const QDateTime& timestamp)
+ : KeyVerificationRequestEvent(
+ basicJson(TypeId, { { "transaction_id"_ls, transactionId },
+ { "from_device"_ls, fromDevice },
+ { "methods"_ls, toJson(methods) },
+ { "timestamp"_ls, toJson(timestamp) } }))
+ {}
+
+ /// The device ID which is initiating the request.
+ QUO_CONTENT_GETTER(QString, fromDevice)
+
+ /// The verification methods supported by the sender.
+ QUO_CONTENT_GETTER(QStringList, methods)
+
+ /// The POSIX timestamp in milliseconds for when the request was
+ /// made. If the request is in the future by more than 5 minutes or
+ /// more than 10 minutes in the past, the message should be ignored
+ /// by the receiver.
+ QUO_CONTENT_GETTER(QDateTime, timestamp)
+};
+
+class QUOTIENT_API KeyVerificationReadyEvent : public KeyVerificationEvent {
+public:
+ QUO_EVENT(KeyVerificationReadyEvent, "m.key.verification.ready")
+
+ using KeyVerificationEvent::KeyVerificationEvent;
+ KeyVerificationReadyEvent(const QString& transactionId,
+ const QString& fromDevice,
+ const QStringList& methods)
+ : KeyVerificationReadyEvent(
+ basicJson(TypeId, { { "transaction_id"_ls, transactionId },
+ { "from_device"_ls, fromDevice },
+ { "methods"_ls, toJson(methods) } }))
+ {}
+
+ /// The device ID which is accepting the request.
+ QUO_CONTENT_GETTER(QString, fromDevice)
+
+ /// The verification methods supported by the sender.
+ QUO_CONTENT_GETTER(QStringList, methods)
+};
+
+/// Begins a key verification process.
+class QUOTIENT_API KeyVerificationStartEvent : public KeyVerificationEvent {
+public:
+ QUO_EVENT(KeyVerificationStartEvent, "m.key.verification.start")
+
+ using KeyVerificationEvent::KeyVerificationEvent;
+ KeyVerificationStartEvent(const QString& transactionId,
+ const QString& fromDevice)
+ : KeyVerificationStartEvent(
+ basicJson(TypeId, { { "transaction_id"_ls, transactionId },
+ { "from_device"_ls, fromDevice },
+ { "method"_ls, SasV1Method },
+ { "hashes"_ls, QJsonArray{ "sha256"_ls } },
+ { "key_agreement_protocols"_ls,
+ QJsonArray{ "curve25519-hkdf-sha256"_ls } },
+ { "message_authentication_codes"_ls,
+ QJsonArray{ "hkdf-hmac-sha256"_ls } },
+ { "short_authentication_string"_ls,
+ QJsonArray{ "decimal"_ls, "emoji"_ls } } }))
+ {}
+
+ /// The device ID which is initiating the process.
+ QUO_CONTENT_GETTER(QString, fromDevice)
+
+ /// The verification method to use.
+ QUO_CONTENT_GETTER(QString, method)
+
+ /// Optional method to use to verify the other user's key with.
+ QUO_CONTENT_GETTER(Omittable<QString>, nextMethod)
+
+ // SAS.V1 methods
+
+ /// The key agreement protocols the sending device understands.
+ /// \note Only exist if method is m.sas.v1
+ QStringList keyAgreementProtocols() const
+ {
+ Q_ASSERT(method() == SasV1Method);
+ return contentPart<QStringList>("key_agreement_protocols"_ls);
+ }
+
+ /// The hash methods the sending device understands.
+ /// \note Only exist if method is m.sas.v1
+ QStringList hashes() const
+ {
+ Q_ASSERT(method() == SasV1Method);
+ return contentPart<QStringList>("hashes"_ls);
+ }
+
+ /// The message authentication codes that the sending device understands.
+ /// \note Only exist if method is m.sas.v1
+ QStringList messageAuthenticationCodes() const
+ {
+ Q_ASSERT(method() == SasV1Method);
+ return contentPart<QStringList>("message_authentication_codes"_ls);
+ }
+
+ /// The SAS methods the sending device (and the sending device's
+ /// user) understands.
+ /// \note Only exist if method is m.sas.v1
+ QString shortAuthenticationString() const
+ {
+ Q_ASSERT(method() == SasV1Method);
+ return contentPart<QString>("short_authentification_string"_ls);
+ }
+};
+
+/// Accepts a previously sent m.key.verification.start message.
+/// Typically sent as a to-device event.
+class QUOTIENT_API KeyVerificationAcceptEvent : public KeyVerificationEvent {
+public:
+ QUO_EVENT(KeyVerificationAcceptEvent, "m.key.verification.accept")
+
+ using KeyVerificationEvent::KeyVerificationEvent;
+ KeyVerificationAcceptEvent(const QString& transactionId,
+ const QString& commitment)
+ : KeyVerificationAcceptEvent(basicJson(
+ TypeId, { { "transaction_id"_ls, transactionId },
+ { "method"_ls, SasV1Method },
+ { "key_agreement_protocol"_ls, "curve25519-hkdf-sha256" },
+ { "hash"_ls, "sha256" },
+ { "message_authentication_code"_ls, "hkdf-hmac-sha256" },
+ { "short_authentication_string"_ls,
+ QJsonArray{ "decimal"_ls, "emoji"_ls, } },
+ { "commitment"_ls, commitment } }))
+ {}
+
+ /// The verification method to use. Must be 'm.sas.v1'.
+ QUO_CONTENT_GETTER(QString, method)
+
+ /// The key agreement protocol the device is choosing to use, out of
+ /// the options in the m.key.verification.start message.
+ QUO_CONTENT_GETTER(QString, keyAgreementProtocol)
+
+ /// The hash method the device is choosing to use, out of the
+ /// options in the m.key.verification.start message.
+ QUO_CONTENT_GETTER_X(QString, hashData, "hash"_ls)
+
+ /// The message authentication code the device is choosing to use, out
+ /// of the options in the m.key.verification.start message.
+ QUO_CONTENT_GETTER(QString, messageAuthenticationCode)
+
+ /// The SAS methods both devices involved in the verification process understand.
+ QUO_CONTENT_GETTER(QStringList, shortAuthenticationString)
+
+ /// The hash (encoded as unpadded base64) of the concatenation of the
+ /// device's ephemeral public key (encoded as unpadded base64) and the
+ /// canonical JSON representation of the m.key.verification.start message.
+ QUO_CONTENT_GETTER(QString, commitment)
+};
+
+class QUOTIENT_API KeyVerificationCancelEvent : public KeyVerificationEvent {
+public:
+ QUO_EVENT(KeyVerificationCancelEvent, "m.key.verification.cancel")
+
+ using KeyVerificationEvent::KeyVerificationEvent;
+ KeyVerificationCancelEvent(const QString& transactionId,
+ const QString& reason)
+ : KeyVerificationCancelEvent(
+ basicJson(TypeId, {
+ { "transaction_id"_ls, transactionId },
+ { "reason"_ls, reason },
+ { "code"_ls, reason } // Not a typo
+ }))
+ {}
+
+ /// A human readable description of the code. The client should only
+ /// rely on this string if it does not understand the code.
+ QUO_CONTENT_GETTER(QString, reason)
+
+ /// The error code for why the process/request was cancelled by the user.
+ QUO_CONTENT_GETTER(QString, code)
+};
+
+/// Sends the ephemeral public key for a device to the partner device.
+/// Typically sent as a to-device event.
+class QUOTIENT_API KeyVerificationKeyEvent : public KeyVerificationEvent {
+public:
+ QUO_EVENT(KeyVerificationKeyEvent, "m.key.verification.key")
+
+ using KeyVerificationEvent::KeyVerificationEvent;
+ KeyVerificationKeyEvent(const QString& transactionId, const QString& key)
+ : KeyVerificationKeyEvent(
+ basicJson(TypeId, { { "transaction_id"_ls, transactionId },
+ { "key"_ls, key } }))
+ {}
+
+ /// The device's ephemeral public key, encoded as unpadded base64.
+ QUO_CONTENT_GETTER(QString, key)
+};
+
+/// Sends the MAC of a device's key to the partner device.
+class QUOTIENT_API KeyVerificationMacEvent : public KeyVerificationEvent {
+public:
+ QUO_EVENT(KeyVerificationMacEvent, "m.key.verification.mac")
+
+ using KeyVerificationEvent::KeyVerificationEvent;
+ KeyVerificationMacEvent(const QString& transactionId, const QString& keys,
+ const QJsonObject& mac)
+ : KeyVerificationMacEvent(
+ basicJson(TypeId, { { "transaction_id"_ls, transactionId },
+ { "keys"_ls, keys },
+ { "mac"_ls, mac } }))
+ {}
+
+ /// The device's ephemeral public key, encoded as unpadded base64.
+ QUO_CONTENT_GETTER(QString, keys)
+
+ QHash<QString, QString> mac() const
+ {
+ return contentPart<QHash<QString, QString>>("mac"_ls);
+ }
+};
+
+class QUOTIENT_API KeyVerificationDoneEvent : public KeyVerificationEvent {
+public:
+ QUO_EVENT(KeyVerificationDoneEvent, "m.key.verification.done")
+
+ using KeyVerificationEvent::KeyVerificationEvent;
+ explicit KeyVerificationDoneEvent(const QString& transactionId)
+ : KeyVerificationDoneEvent(
+ basicJson(TypeId, { { "transaction_id"_ls, transactionId } }))
+ {}
+};
+} // namespace Quotient
diff --git a/lib/events/reactionevent.cpp b/lib/events/reactionevent.cpp
deleted file mode 100644
index 003c8ead..00000000
--- a/lib/events/reactionevent.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/******************************************************************************
- * 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
- */
-
-#include "reactionevent.h"
-
-using namespace Quotient;
-
-void JsonObjectConverter<EventRelation>::dumpTo(
- QJsonObject& jo, const EventRelation& pod)
-{
- if (pod.type.isEmpty()) {
- qCWarning(MAIN) << "Empty relation type; won't dump to JSON";
- return;
- }
- jo.insert(QStringLiteral("rel_type"), pod.type);
- jo.insert(EventIdKey, pod.eventId);
- if (pod.type == EventRelation::Annotation())
- jo.insert(QStringLiteral("key"), pod.key);
-}
-
-void JsonObjectConverter<EventRelation>::fillFrom(
- const QJsonObject& jo, EventRelation& pod)
-{
- // The experimental logic for generic relationships (MSC1849)
- fromJson(jo["rel_type"_ls], pod.type);
- fromJson(jo[EventIdKeyL], pod.eventId);
- if (pod.type == EventRelation::Annotation())
- fromJson(jo["key"_ls], pod.key);
-}
diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h
index 75c6528c..8d873441 100644
--- a/lib/events/reactionevent.h
+++ b/lib/events/reactionevent.h
@@ -1,73 +1,14 @@
-/******************************************************************************
- * 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
- */
+// SPDX-FileCopyrightText: 2019 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "roomevent.h"
+#include "eventrelation.h"
namespace Quotient {
-struct EventRelation {
- using reltypeid_t = const char*;
- static constexpr reltypeid_t Reply() { return "m.in_reply_to"; }
- static constexpr reltypeid_t Annotation() { return "m.annotation"; }
- static constexpr reltypeid_t Replacement() { return "m.replace"; }
-
- QString type;
- QString eventId;
- QString key = {}; // Only used for m.annotation for now
-
- static EventRelation replyTo(QString eventId)
- {
- return { Reply(), std::move(eventId) };
- }
- static EventRelation annotate(QString eventId, QString key)
- {
- return { Annotation(), std::move(eventId), std::move(key) };
- }
- static EventRelation replace(QString eventId)
- {
- return { Replacement(), std::move(eventId) };
- }
-};
-template <>
-struct JsonObjectConverter<EventRelation> {
- static void dumpTo(QJsonObject& jo, const EventRelation& pod);
- static void fillFrom(const QJsonObject& jo, EventRelation& pod);
-};
-
-class ReactionEvent : public RoomEvent {
-public:
- DEFINE_EVENT_TYPEID("m.reaction", ReactionEvent)
-
- explicit ReactionEvent(const EventRelation& value)
- : RoomEvent(typeId(), matrixTypeId(),
- { { QStringLiteral("m.relates_to"), toJson(value) } })
- {}
- explicit ReactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj) {}
- EventRelation relation() const
- {
- return content<EventRelation>(QStringLiteral("m.relates_to"));
- }
-
-private:
- EventRelation _relation;
-};
-REGISTER_EVENT_TYPE(ReactionEvent)
+DEFINE_SIMPLE_EVENT(ReactionEvent, RoomEvent, "m.reaction", EventRelation,
+ relation, "m.relates_to")
} // namespace Quotient
diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp
index bf050cb2..d8f9fa0b 100644
--- a/lib/events/receiptevent.cpp
+++ b/lib/events/receiptevent.cpp
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2016 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2018 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
/*
Example of a Receipt Event:
@@ -35,31 +20,49 @@ Example of a Receipt Event:
#include "receiptevent.h"
-#include "converters.h"
#include "logging.h"
using namespace Quotient;
-ReceiptEvent::ReceiptEvent(const QJsonObject& obj) : Event(typeId(), obj)
+// The library loads the event-ids-to-receipts JSON map into a vector because
+// map lookups are not used and vectors are massively faster. Same goes for
+// de-/serialization of ReceiptsForEvent::receipts.
+// (XXX: would this be generally preferred across CS API JSON maps?..)
+QJsonObject Quotient::toJson(const EventsWithReceipts& ewrs)
{
- const auto& contents = contentJson();
- _eventsWithReceipts.reserve(contents.size());
- for (auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt) {
+ QJsonObject json;
+ for (const auto& e : ewrs) {
+ QJsonObject receiptsJson;
+ for (const auto& r : e.receipts)
+ receiptsJson.insert(r.userId,
+ QJsonObject { { "ts"_ls, toJson(r.timestamp) } });
+ json.insert(e.evtId, QJsonObject { { "m.read"_ls, receiptsJson } });
+ }
+ return json;
+}
+
+template<>
+EventsWithReceipts Quotient::fromJson(const QJsonObject& json)
+{
+ EventsWithReceipts result;
+ result.reserve(json.size());
+ for (auto eventIt = json.begin(); eventIt != json.end(); ++eventIt) {
if (eventIt.key().isEmpty()) {
qCWarning(EPHEMERAL)
<< "ReceiptEvent has an empty event id, skipping";
- qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << contents;
+ qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << json;
continue;
}
- const QJsonObject reads =
+ const auto reads =
eventIt.value().toObject().value("m.read"_ls).toObject();
- QVector<Receipt> receipts;
- receipts.reserve(reads.size());
+ QVector<UserTimestamp> usersAtEvent;
+ usersAtEvent.reserve(reads.size());
for (auto userIt = reads.begin(); userIt != reads.end(); ++userIt) {
- const QJsonObject user = userIt.value().toObject();
- receipts.push_back(
+ const auto user = userIt.value().toObject();
+ usersAtEvent.push_back(
{ userIt.key(), fromJson<QDateTime>(user["ts"_ls]) });
}
- _eventsWithReceipts.push_back({ eventIt.key(), std::move(receipts) });
+ result.push_back({ eventIt.key(), std::move(usersAtEvent) });
}
+ return result;
}
diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h
index dd54a476..b87e00f6 100644
--- a/lib/events/receiptevent.h
+++ b/lib/events/receiptevent.h
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2016 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2018 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
@@ -24,28 +9,27 @@
#include <QtCore/QVector>
namespace Quotient {
-struct Receipt {
+struct UserTimestamp {
QString userId;
QDateTime timestamp;
};
struct ReceiptsForEvent {
QString evtId;
- QVector<Receipt> receipts;
+ QVector<UserTimestamp> receipts;
};
using EventsWithReceipts = QVector<ReceiptsForEvent>;
-class ReceiptEvent : public Event {
-public:
- DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent)
- explicit ReceiptEvent(const QJsonObject& obj);
+template <>
+QUOTIENT_API EventsWithReceipts fromJson(const QJsonObject& json);
+QUOTIENT_API QJsonObject toJson(const EventsWithReceipts& ewrs);
- const EventsWithReceipts& eventsWithReceipts() const
- {
- return _eventsWithReceipts;
- }
+class QUOTIENT_API ReceiptEvent
+ : public EventTemplate<ReceiptEvent, Event, EventsWithReceipts> {
+public:
+ QUO_EVENT(ReceiptEvent, "m.receipt")
+ using EventTemplate::EventTemplate;
-private:
- EventsWithReceipts _eventsWithReceipts;
+ [[deprecated("Use content() instead")]]
+ EventsWithReceipts eventsWithReceipts() const { return content(); }
};
-REGISTER_EVENT_TYPE(ReceiptEvent)
} // namespace Quotient
diff --git a/lib/events/redactionevent.cpp b/lib/events/redactionevent.cpp
deleted file mode 100644
index bf467718..00000000
--- a/lib/events/redactionevent.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "redactionevent.h"
diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h
index 3b3af18e..a2e0b73b 100644
--- a/lib/events/redactionevent.h
+++ b/lib/events/redactionevent.h
@@ -1,38 +1,21 @@
-/******************************************************************************
- * Copyright (C) 2017 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
- */
+// SPDX-FileCopyrightText: 2017 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "roomevent.h"
namespace Quotient {
-class RedactionEvent : public RoomEvent {
+class QUOTIENT_API RedactionEvent : public RoomEvent {
public:
- DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent)
+ QUO_EVENT(RedactionEvent, "m.room.redaction")
- explicit RedactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj)
- {}
+ using RoomEvent::RoomEvent;
QString redactedEvent() const
{
return fullJson()["redacts"_ls].toString();
}
- QString reason() const { return contentJson()["reason"_ls].toString(); }
+ QUO_CONTENT_GETTER(QString, reason)
};
-REGISTER_EVENT_TYPE(RedactionEvent)
} // namespace Quotient
diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h
index c2100eaa..1986f852 100644
--- a/lib/events/roomavatarevent.h
+++ b/lib/events/roomavatarevent.h
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2017 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
- */
+// SPDX-FileCopyrightText: 2017 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
@@ -22,28 +7,17 @@
#include "stateevent.h"
namespace Quotient {
-class RoomAvatarEvent : public StateEvent<EventContent::ImageContent> {
+class QUOTIENT_API RoomAvatarEvent
+ : public KeylessStateEventBase<RoomAvatarEvent,
+ EventContent::ImageContent> {
// It's a bit of an overkill to use a full-fledged ImageContent
// because in reality m.room.avatar usually only has a single URL,
// without a thumbnail. But The Spec says there be thumbnails, and
- // we follow The Spec.
+ // we follow The Spec (and ImageContent is very convenient to reuse here).
public:
- DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent)
- explicit RoomAvatarEvent(const QJsonObject& obj) : StateEvent(typeId(), obj)
- {}
- explicit RoomAvatarEvent(const EventContent::ImageContent& avatar)
- : StateEvent(typeId(), matrixTypeId(), QString(), avatar)
- {}
- // A replica of EventContent::ImageInfo constructor
- explicit RoomAvatarEvent(const QUrl& u, qint64 fileSize = -1,
- QMimeType mimeType = {},
- const QSize& imageSize = {},
- const QString& originalFilename = {})
- : RoomAvatarEvent(EventContent::ImageContent {
- u, fileSize, mimeType, imageSize, originalFilename })
- {}
+ QUO_EVENT(RoomAvatarEvent, "m.room.avatar")
+ using KeylessStateEventBase::KeylessStateEventBase;
- QUrl url() const { return content().url; }
+ QUrl url() const { return content().url(); }
};
-REGISTER_EVENT_TYPE(RoomAvatarEvent)
} // namespace Quotient
diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h
index fadfece0..c73bc92a 100644
--- a/lib/events/roomcanonicalaliasevent.h
+++ b/lib/events/roomcanonicalaliasevent.h
@@ -1,78 +1,44 @@
-/******************************************************************************
- * Copyright (C) 2020 QMatrixClient project
- *
- * 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-FileCopyrightText: 2020 Ram Nad <ramnad1999@gmail.com>
+// SPDX-FileCopyrightText: 2020 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "stateevent.h"
namespace Quotient {
-namespace EventContent{
- class AliasesEventContent {
-
- public:
-
- template<typename T1, typename T2>
- AliasesEventContent(T1&& canonicalAlias, T2&& altAliases)
- : canonicalAlias(std::forward<T1>(canonicalAlias))
- , altAliases(std::forward<T2>(altAliases))
- { }
-
- AliasesEventContent(const QJsonObject& json)
- : canonicalAlias(fromJson<QString>(json["alias"]))
- , altAliases(fromJson<QStringList>(json["alt_aliases"]))
- { }
-
- auto toJson() const
- {
- QJsonObject jo;
- addParam<IfNotEmpty>(jo, QStringLiteral("alias"), canonicalAlias);
- addParam<IfNotEmpty>(jo, QStringLiteral("alt_aliases"), altAliases);
- return jo;
- }
-
+namespace EventContent {
+ struct AliasesEventContent {
QString canonicalAlias;
QStringList altAliases;
};
} // namespace EventContent
-class RoomCanonicalAliasEvent
- : public StateEvent<EventContent::AliasesEventContent> {
+template<>
+inline EventContent::AliasesEventContent fromJson(const QJsonObject& jo)
+{
+ return EventContent::AliasesEventContent {
+ fromJson<QString>(jo["alias"_ls]),
+ fromJson<QStringList>(jo["alt_aliases"_ls])
+ };
+}
+template<>
+inline auto toJson(const EventContent::AliasesEventContent& c)
+{
+ QJsonObject jo;
+ addParam<IfNotEmpty>(jo, QStringLiteral("alias"), c.canonicalAlias);
+ addParam<IfNotEmpty>(jo, QStringLiteral("alt_aliases"), c.altAliases);
+ return jo;
+}
+
+class QUOTIENT_API RoomCanonicalAliasEvent
+ : public KeylessStateEventBase<RoomCanonicalAliasEvent,
+ EventContent::AliasesEventContent> {
public:
- DEFINE_EVENT_TYPEID("m.room.canonical_alias", RoomCanonicalAliasEvent)
-
- explicit RoomCanonicalAliasEvent(const QJsonObject& obj)
- : StateEvent(typeId(), obj)
- { }
-
- explicit RoomCanonicalAliasEvent(const QString& canonicalAlias,
- const QStringList& altAliases = {})
- : StateEvent(typeId(), matrixTypeId(), QString(),
- canonicalAlias, altAliases)
- { }
-
- explicit RoomCanonicalAliasEvent(QString&& canonicalAlias,
- QStringList&& altAliases = {})
- : StateEvent(typeId(), matrixTypeId(), QString(),
- std::move(canonicalAlias), std::move(altAliases))
- { }
+ QUO_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias")
+ using KeylessStateEventBase::KeylessStateEventBase;
QString alias() const { return content().canonicalAlias; }
QStringList altAliases() const { return content().altAliases; }
};
-REGISTER_EVENT_TYPE(RoomCanonicalAliasEvent)
} // namespace Quotient
diff --git a/lib/events/roomcreateevent.cpp b/lib/events/roomcreateevent.cpp
index c72b5bc2..3b5024d5 100644
--- a/lib/events/roomcreateevent.cpp
+++ b/lib/events/roomcreateevent.cpp
@@ -1,43 +1,40 @@
-/******************************************************************************
- * Copyright (C) 2019 QMatrixClient project
- *
- * 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-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "roomcreateevent.h"
using namespace Quotient;
+template <>
+RoomType Quotient::fromJson(const QJsonValue& jv)
+{
+ return enumFromJsonString(jv.toString(), RoomTypeStrings,
+ RoomType::Undefined);
+}
+
bool RoomCreateEvent::isFederated() const
{
- return fromJson<bool>(contentJson()["m.federate"_ls]);
+ return contentPart<bool>("m.federate"_ls);
}
QString RoomCreateEvent::version() const
{
- return fromJson<QString>(contentJson()["room_version"_ls]);
+ return contentPart<QString>("room_version"_ls);
}
RoomCreateEvent::Predecessor RoomCreateEvent::predecessor() const
{
- const auto predJson = contentJson()["predecessor"_ls].toObject();
- return { fromJson<QString>(predJson["room_id"_ls]),
- fromJson<QString>(predJson["event_id"_ls]) };
+ const auto predJson = contentPart<QJsonObject>("predecessor"_ls);
+ return { fromJson<QString>(predJson[RoomIdKeyL]),
+ fromJson<QString>(predJson[EventIdKeyL]) };
}
bool RoomCreateEvent::isUpgrade() const
{
return contentJson().contains("predecessor"_ls);
}
+
+RoomType RoomCreateEvent::roomType() const
+{
+ return contentPart<RoomType>("type"_ls);
+}
diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h
index 91aefe9e..5968e187 100644
--- a/lib/events/roomcreateevent.h
+++ b/lib/events/roomcreateevent.h
@@ -1,34 +1,17 @@
-/******************************************************************************
- * Copyright (C) 2019 QMatrixClient project
- *
- * 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-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "stateevent.h"
+#include "quotient_common.h"
namespace Quotient {
-class RoomCreateEvent : public StateEventBase {
+class QUOTIENT_API RoomCreateEvent : public StateEvent {
public:
- DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent)
+ QUO_EVENT(RoomCreateEvent, "m.room.create")
- explicit RoomCreateEvent() : StateEventBase(typeId(), matrixTypeId()) {}
- explicit RoomCreateEvent(const QJsonObject& obj)
- : StateEventBase(typeId(), obj)
- {}
+ using StateEvent::StateEvent;
struct Predecessor {
QString roomId;
@@ -39,6 +22,6 @@ public:
QString version() const;
Predecessor predecessor() const;
bool isUpgrade() const;
+ RoomType roomType() const;
};
-REGISTER_EVENT_TYPE(RoomCreateEvent)
} // namespace Quotient
diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp
index a59cd6e0..e98cb591 100644
--- a/lib/events/roomevent.cpp
+++ b/lib/events/roomevent.cpp
@@ -1,43 +1,18 @@
-/******************************************************************************
- * Copyright (C) 2018 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
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "roomevent.h"
-#include "converters.h"
#include "logging.h"
#include "redactionevent.h"
using namespace Quotient;
-[[maybe_unused]] static auto roomEventTypeInitialised =
- Event::factory_t::chainFactory<RoomEvent>();
-
-RoomEvent::RoomEvent(Type type, event_mtype_t matrixType,
- const QJsonObject& contentJson)
- : Event(type, matrixType, contentJson)
-{}
-
-RoomEvent::RoomEvent(Type type, const QJsonObject& json) : Event(type, json)
+RoomEvent::RoomEvent(const QJsonObject& json) : Event(json)
{
- const auto unsignedData = json[UnsignedKeyL].toObject();
- const auto redaction = unsignedData[RedactedCauseKeyL];
- if (redaction.isObject())
- _redactedBecause = makeEvent<RedactionEvent>(redaction.toObject());
+ if (const auto redaction = unsignedPart<QJsonObject>(RedactedCauseKeyL);
+ !redaction.isEmpty())
+ _redactedBecause = loadEvent<RedactionEvent>(redaction);
}
RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job
@@ -51,24 +26,24 @@ QDateTime RoomEvent::originTimestamp() const
QString RoomEvent::roomId() const
{
- return fullJson()["room_id"_ls].toString();
+ return fullJson()[RoomIdKeyL].toString();
}
QString RoomEvent::senderId() const
{
- return fullJson()["sender"_ls].toString();
+ return fullJson()[SenderKeyL].toString();
}
bool RoomEvent::isReplaced() const
{
- return unsignedJson()["m.relations"_ls].toObject().contains("m.replace");
+ return unsignedPart<QJsonObject>("m.relations"_ls).contains("m.replace");
}
QString RoomEvent::replacedBy() const
{
// clang-format off
- return unsignedJson()["m.relations"_ls].toObject()
- .value("m.replace").toObject()
+ return unsignedPart<QJsonObject>("m.relations"_ls)
+ .value("m.replace"_ls).toObject()
.value(EventIdKeyL).toString();
// clang-format on
}
@@ -80,7 +55,7 @@ QString RoomEvent::redactionReason() const
QString RoomEvent::transactionId() const
{
- return unsignedJson()["transaction_id"_ls].toString();
+ return unsignedPart<QString>("transaction_id"_ls);
}
QString RoomEvent::stateKey() const
@@ -90,12 +65,12 @@ QString RoomEvent::stateKey() const
void RoomEvent::setRoomId(const QString& roomId)
{
- editJson().insert(QStringLiteral("room_id"), roomId);
+ editJson().insert(RoomIdKey, roomId);
}
void RoomEvent::setSender(const QString& senderId)
{
- editJson().insert(QStringLiteral("sender"), senderId);
+ editJson().insert(SenderKey, senderId);
}
void RoomEvent::setTransactionId(const QString& txnId)
@@ -115,24 +90,23 @@ void RoomEvent::addId(const QString& newId)
Q_ASSERT(id() == newId);
}
-QJsonObject makeCallContentJson(const QString& callId, int version,
- QJsonObject content)
+void RoomEvent::dumpTo(QDebug dbg) const
{
- content.insert(QStringLiteral("call_id"), callId);
- content.insert(QStringLiteral("version"), version);
- return content;
+ Event::dumpTo(dbg);
+ dbg << " (made at " << originTimestamp().toString(Qt::ISODate) << ')';
}
-CallEventBase::CallEventBase(Type type, event_mtype_t matrixType,
- const QString& callId, int version,
- const QJsonObject& contentJson)
- : RoomEvent(type, matrixType,
- makeCallContentJson(callId, version, contentJson))
-{}
+#ifdef Quotient_E2EE_ENABLED
+void RoomEvent::setOriginalEvent(event_ptr_tt<RoomEvent>&& originalEvent)
+{
+ _originalEvent = std::move(originalEvent);
+}
-CallEventBase::CallEventBase(Event::Type type, const QJsonObject& json)
- : RoomEvent(type, json)
+const QJsonObject RoomEvent::encryptedJson() const
{
- if (callId().isEmpty())
- qCWarning(EVENTS) << id() << "is a call event with an empty call id";
+ if(!_originalEvent) {
+ return {};
+ }
+ return _originalEvent->fullJson();
}
+#endif
diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h
index 621652cb..203434f6 100644
--- a/lib/events/roomevent.h
+++ b/lib/events/roomevent.h
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2018 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
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
@@ -25,33 +10,22 @@
namespace Quotient {
class RedactionEvent;
-/** This class corresponds to m.room.* events */
-class RoomEvent : public Event {
- Q_GADGET
- Q_PROPERTY(QString id READ id)
- Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT)
- Q_PROPERTY(QString roomId READ roomId CONSTANT)
- Q_PROPERTY(QString senderId READ senderId CONSTANT)
- Q_PROPERTY(QString redactionReason READ redactionReason)
- Q_PROPERTY(bool isRedacted READ isRedacted)
- Q_PROPERTY(QString transactionId READ transactionId WRITE setTransactionId)
+// That check could look into Event and find most stuff already deleted...
+// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
+class QUOTIENT_API RoomEvent : public Event {
public:
- using factory_t = EventFactory<RoomEvent>;
+ QUO_BASE_EVENT(RoomEvent, {}, Event::BaseMetaType)
- // RedactionEvent is an incomplete type here so we cannot inline
- // constructors and destructors and we cannot use 'using'.
- RoomEvent(Type type, event_mtype_t matrixType,
- const QJsonObject& contentJson = {});
- RoomEvent(Type type, const QJsonObject& json);
- ~RoomEvent() override;
+ ~RoomEvent() override; // Don't inline this - see the private section
QString id() const;
QDateTime originTimestamp() const;
- [[deprecated("Use originTimestamp()")]] QDateTime timestamp() const {
- return originTimestamp();
- }
QString roomId() const;
QString senderId() const;
+ //! \brief Determine whether the event has been replaced
+ //!
+ //! \return true if this event has been overridden by another event
+ //! with `"rel_type": "m.replace"`; false otherwise
bool isReplaced() const;
QString replacedBy() const;
bool isRedacted() const { return bool(_redactedBecause); }
@@ -63,48 +37,48 @@ public:
QString transactionId() const;
QString stateKey() const;
+ //! \brief Fill the pending event object with the room id
void setRoomId(const QString& roomId);
+ //! \brief Fill the pending event object with the sender id
void setSender(const QString& senderId);
-
- /**
- * Sets the transaction id for locally created events. This should be
- * done before the event is exposed to any code using the respective
- * Q_PROPERTY.
- *
- * \param txnId - transaction id, normally obtained from
- * Connection::generateTxnId()
- */
+ //! \brief Fill the pending event object with the transaction id
+ //! \param txnId - transaction id, normally obtained from
+ //! Connection::generateTxnId()
void setTransactionId(const QString& txnId);
- /**
- * Sets event id for locally created events
- *
- * When a new event is created locally, it has no server id yet.
- * This function allows to add the id once the confirmation from
- * the server is received. There should be no id set previously
- * in the event. It's the responsibility of the code calling addId()
- * to notify clients that use Q_PROPERTY(id) about its change
- */
+ //! \brief Add an event id to locally created events after they are sent
+ //!
+ //! When a new event is created locally, it has no id; the homeserver
+ //! assigns it once the event is sent. This function allows to add the id
+ //! once the confirmation from the server is received. There should be no id
+ //! set previously in the event. It's the responsibility of the code calling
+ //! addId() to notify clients about the change; there's no signal or
+ //! callback for that in RoomEvent.
void addId(const QString& newId);
+#ifdef Quotient_E2EE_ENABLED
+ void setOriginalEvent(event_ptr_tt<RoomEvent>&& originalEvent);
+ const RoomEvent* originalEvent() const { return _originalEvent.get(); }
+ const QJsonObject encryptedJson() const;
+#endif
+
+protected:
+ explicit RoomEvent(const QJsonObject& json);
+ void dumpTo(QDebug dbg) const override;
+
private:
+ // RedactionEvent is an incomplete type here so we cannot inline
+ // constructors using it and also destructors (with 'using', in particular).
event_ptr_tt<RedactionEvent> _redactedBecause;
+
+#ifdef Quotient_E2EE_ENABLED
+ event_ptr_tt<RoomEvent> _originalEvent;
+#endif
};
using RoomEventPtr = event_ptr_tt<RoomEvent>;
using RoomEvents = EventsArray<RoomEvent>;
using RoomEventsRange = Range<RoomEvents>;
-class CallEventBase : public RoomEvent {
-public:
- CallEventBase(Type type, event_mtype_t matrixType, const QString& callId,
- int version, const QJsonObject& contentJson = {});
- CallEventBase(Type type, const QJsonObject& json);
- ~CallEventBase() override = default;
- bool isCallEvent() const override { return true; }
-
- QString callId() const { return content<QString>("call_id"_ls); }
- int version() const { return content<int>("version"_ls); }
-};
} // namespace Quotient
Q_DECLARE_METATYPE(Quotient::RoomEvent*)
Q_DECLARE_METATYPE(const Quotient::RoomEvent*)
diff --git a/lib/events/roomkeyevent.cpp b/lib/events/roomkeyevent.cpp
deleted file mode 100644
index 66580430..00000000
--- a/lib/events/roomkeyevent.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "roomkeyevent.h"
-
-using namespace Quotient;
-
-RoomKeyEvent::RoomKeyEvent(const QJsonObject &obj) : Event(typeId(), obj)
-{
- if (roomId().isEmpty())
- qCWarning(E2EE) << "Room key event has empty room id";
-}
diff --git a/lib/events/roomkeyevent.h b/lib/events/roomkeyevent.h
index 679cbf7c..dad5df8b 100644
--- a/lib/events/roomkeyevent.h
+++ b/lib/events/roomkeyevent.h
@@ -1,19 +1,33 @@
+// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
#pragma once
#include "event.h"
namespace Quotient {
-class RoomKeyEvent : public Event
+class QUOTIENT_API RoomKeyEvent : public Event
{
public:
- DEFINE_EVENT_TYPEID("m.room_key", RoomKeyEvent)
+ QUO_EVENT(RoomKeyEvent, "m.room_key")
- RoomKeyEvent(const QJsonObject& obj);
+ using Event::Event;
+ explicit RoomKeyEvent(const QString& algorithm, const QString& roomId,
+ const QString& sessionId, const QString& sessionKey)
+ : Event(basicJson(TypeId, {
+ { "algorithm", algorithm },
+ { "room_id", roomId },
+ { "session_id", sessionId },
+ { "session_key", sessionKey },
+ }))
+ {}
- QString algorithm() const { return content<QString>("algorithm"_ls); }
- QString roomId() const { return content<QString>("room_id"_ls); }
- QString sessionId() const { return content<QString>("session_id"_ls); }
- QString sessionKey() const { return content<QString>("session_key"_ls); }
+ QUO_CONTENT_GETTER(QString, algorithm)
+ QUO_CONTENT_GETTER(QString, roomId)
+ QUO_CONTENT_GETTER(QString, sessionId)
+ QByteArray sessionKey() const
+ {
+ return contentPart<QString>("session_key"_ls).toLatin1();
+ }
};
-REGISTER_EVENT_TYPE(RoomKeyEvent)
} // namespace Quotient
diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp
index 3193a54d..4e7eae1b 100644
--- a/lib/events/roommemberevent.cpp
+++ b/lib/events/roommemberevent.cpp
@@ -1,47 +1,20 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2017 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-FileCopyrightText: 2019 Karol Kosek <krkkx@protonmail.com>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "roommemberevent.h"
-
-#include "converters.h"
#include "logging.h"
-#include <array>
-
-static const std::array<QString, 5> membershipStrings = {
- { QStringLiteral("invite"), QStringLiteral("join"), QStringLiteral("knock"),
- QStringLiteral("leave"), QStringLiteral("ban") }
-};
-
namespace Quotient {
template <>
-struct JsonConverter<MembershipType> {
- static MembershipType load(const QJsonValue& jv)
+struct JsonConverter<Membership> {
+ static Membership load(const QJsonValue& jv)
{
- const auto& membershipString = jv.toString();
- for (auto it = membershipStrings.begin(); it != membershipStrings.end();
- ++it)
- if (membershipString == *it)
- return MembershipType(it - membershipStrings.begin());
+ if (const auto& ms = jv.toString(); !ms.isEmpty())
+ return flagFromJsonString<Membership>(ms, MembershipStrings);
- if (!membershipString.isEmpty())
- qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString;
- return MembershipType::Undefined;
+ qCWarning(EVENTS) << "Empty membership state";
+ return Membership::Invalid;
}
};
} // namespace Quotient
@@ -49,25 +22,29 @@ struct JsonConverter<MembershipType> {
using namespace Quotient;
MemberEventContent::MemberEventContent(const QJsonObject& json)
- : membership(fromJson<MembershipType>(json["membership"_ls]))
+ : membership(fromJson<Membership>(json["membership"_ls]))
, isDirect(json["is_direct"_ls].toBool())
- , displayName(sanitized(json["displayname"_ls].toString()))
- , avatarUrl(json["avatar_url"_ls].toString())
+ , displayName(fromJson<Omittable<QString>>(json["displayname"_ls]))
+ , avatarUrl(fromJson<Omittable<QString>>(json["avatar_url"_ls]))
, reason(json["reason"_ls].toString())
-{}
+{
+ if (displayName)
+ displayName = sanitized(*displayName);
+}
-void MemberEventContent::fillJson(QJsonObject* o) const
+QJsonObject MemberEventContent::toJson() const
{
- Q_ASSERT(o);
- Q_ASSERT_X(membership != MembershipType::Undefined, __FUNCTION__,
- "The key 'membership' must be explicit in MemberEventContent");
- if (membership != MembershipType::Undefined)
- o->insert(QStringLiteral("membership"), membershipStrings[membership]);
- o->insert(QStringLiteral("displayname"), displayName);
- if (avatarUrl.isValid())
- o->insert(QStringLiteral("avatar_url"), avatarUrl.toString());
+ QJsonObject o;
+ if (membership != Membership::Invalid)
+ o.insert(QStringLiteral("membership"),
+ flagToJsonString(membership, MembershipStrings));
+ if (displayName)
+ o.insert(QStringLiteral("displayname"), *displayName);
+ if (avatarUrl && avatarUrl->isValid())
+ o.insert(QStringLiteral("avatar_url"), avatarUrl->toString());
if (!reason.isEmpty())
- o->insert(QStringLiteral("reason"), reason);
+ o.insert(QStringLiteral("reason"), reason);
+ return o;
}
bool RoomMemberEvent::changesMembership() const
@@ -77,47 +54,49 @@ bool RoomMemberEvent::changesMembership() const
bool RoomMemberEvent::isInvite() const
{
- return membership() == MembershipType::Invite && changesMembership();
+ return membership() == Membership::Invite && changesMembership();
}
bool RoomMemberEvent::isRejectedInvite() const
{
- return membership() == MembershipType::Leave && prevContent()
- && prevContent()->membership == MembershipType::Invite;
+ return membership() == Membership::Leave && prevContent()
+ && prevContent()->membership == Membership::Invite;
}
bool RoomMemberEvent::isJoin() const
{
- return membership() == MembershipType::Join && changesMembership();
+ return membership() == Membership::Join && changesMembership();
}
bool RoomMemberEvent::isLeave() const
{
- return membership() == MembershipType::Leave && prevContent()
+ return membership() == Membership::Leave && prevContent()
&& prevContent()->membership != membership()
- && prevContent()->membership != MembershipType::Ban
- && prevContent()->membership != MembershipType::Invite;
+ && prevContent()->membership != Membership::Ban
+ && prevContent()->membership != Membership::Invite;
}
bool RoomMemberEvent::isBan() const
{
- return membership() == MembershipType::Ban && changesMembership();
+ return membership() == Membership::Ban && changesMembership();
}
bool RoomMemberEvent::isUnban() const
{
- return membership() == MembershipType::Leave && prevContent()
- && prevContent()->membership == MembershipType::Ban;
+ return membership() == Membership::Leave && prevContent()
+ && prevContent()->membership == Membership::Ban;
}
bool RoomMemberEvent::isRename() const
{
- auto prevName = prevContent() ? prevContent()->displayName : QString();
- return displayName() != prevName;
+ return prevContent() && prevContent()->displayName
+ ? newDisplayName() != *prevContent()->displayName
+ : newDisplayName().has_value();
}
bool RoomMemberEvent::isAvatarUpdate() const
{
- auto prevAvatarUrl = prevContent() ? prevContent()->avatarUrl : QUrl();
- return avatarUrl() != prevAvatarUrl;
+ return prevContent() && prevContent()->avatarUrl
+ ? newAvatarUrl() != *prevContent()->avatarUrl
+ : newAvatarUrl().has_value();
}
diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h
index 783b8207..9f063136 100644
--- a/lib/events/roommemberevent.h
+++ b/lib/events/roommemberevent.h
@@ -1,91 +1,57 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de>
+// SPDX-FileCopyrightText: 2017 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-FileCopyrightText: 2019 Karol Kosek <krkkx@protonmail.com>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
-#include "eventcontent.h"
#include "stateevent.h"
+#include "quotient_common.h"
namespace Quotient {
-class MemberEventContent : public EventContent::Base {
+class QUOTIENT_API MemberEventContent {
public:
- enum MembershipType : size_t {
- Invite = 0,
- Join,
- Knock,
- Leave,
- Ban,
- Undefined
- };
+ using MembershipType
+ [[deprecated("Use Quotient::Membership instead")]] = Membership;
- explicit MemberEventContent(MembershipType mt = Join) : membership(mt) {}
+ QUO_IMPLICIT MemberEventContent(Membership ms) : membership(ms) {}
explicit MemberEventContent(const QJsonObject& json);
+ QJsonObject toJson() const;
- MembershipType membership;
+ Membership membership;
+ /// (Only for invites) Whether the invite is to a direct chat
bool isDirect = false;
- QString displayName;
- QUrl avatarUrl;
+ Omittable<QString> displayName;
+ Omittable<QUrl> avatarUrl;
QString reason;
-
-protected:
- void fillJson(QJsonObject* o) const override;
};
-using MembershipType = MemberEventContent::MembershipType;
+using MembershipType [[deprecated("Use Membership instead")]] = Membership;
-class RoomMemberEvent : public StateEvent<MemberEventContent> {
+class QUOTIENT_API RoomMemberEvent
+ : public KeyedStateEventBase<RoomMemberEvent, MemberEventContent> {
Q_GADGET
public:
- DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent)
-
- using MembershipType = MemberEventContent::MembershipType;
+ QUO_EVENT(RoomMemberEvent, "m.room.member")
- explicit RoomMemberEvent(const QJsonObject& obj) : StateEvent(typeId(), obj)
- {}
- [[deprecated("Use RoomMemberEvent(userId, contentArgs) instead")]]
- RoomMemberEvent(MemberEventContent&& c)
- : StateEvent(typeId(), matrixTypeId(), QString(), c)
- {}
- template <typename... ArgTs>
- RoomMemberEvent(const QString& userId, ArgTs&&... contentArgs)
- : StateEvent(typeId(), matrixTypeId(), userId,
- std::forward<ArgTs>(contentArgs)...)
- {}
+ using MembershipType
+ [[deprecated("Use Quotient::Membership instead")]] = Membership;
- /// A special constructor to create unknown RoomMemberEvents
- /**
- * This is needed in order to use RoomMemberEvent as a "base event
- * class" in cases like GetMembersByRoomJob when RoomMemberEvents
- * (rather than RoomEvents or StateEvents) are resolved from JSON.
- * For such cases loadEvent<> requires an underlying class to be
- * constructible with unknownTypeId() instead of its genuine id.
- * Don't use it directly.
- * \sa GetMembersByRoomJob, loadEvent, unknownTypeId
- */
- RoomMemberEvent(Type type, const QJsonObject& fullJson)
- : StateEvent(type, fullJson)
- {}
+ using KeyedStateEventBase::KeyedStateEventBase;
- MembershipType membership() const { return content().membership; }
- QString userId() const { return fullJson()[StateKeyKeyL].toString(); }
+ Membership membership() const { return content().membership; }
+ QString userId() const { return stateKey(); }
bool isDirect() const { return content().isDirect; }
- QString displayName() const { return content().displayName; }
- QUrl avatarUrl() const { return content().avatarUrl; }
+ Omittable<QString> newDisplayName() const { return content().displayName; }
+ Omittable<QUrl> newAvatarUrl() const { return content().avatarUrl; }
+ [[deprecated("Use newDisplayName() instead")]] QString displayName() const
+ {
+ return newDisplayName().value_or(QString());
+ }
+ [[deprecated("Use newAvatarUrl() instead")]] QUrl avatarUrl() const
+ {
+ return newAvatarUrl().value_or(QUrl());
+ }
QString reason() const { return content().reason; }
bool changesMembership() const;
bool isBan() const;
@@ -96,20 +62,5 @@ public:
bool isLeave() const;
bool isRename() const;
bool isAvatarUpdate() const;
-
-private:
- Q_ENUM(MembershipType)
};
-
-template <>
-class EventFactory<RoomMemberEvent> {
-public:
- static event_ptr_tt<RoomMemberEvent> make(const QJsonObject& json,
- const QString&)
- {
- return makeEvent<RoomMemberEvent>(json);
- }
-};
-
-REGISTER_EVENT_TYPE(RoomMemberEvent)
} // namespace Quotient
diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp
index de499e7c..df4840b3 100644
--- a/lib/events/roommessageevent.cpp
+++ b/lib/events/roommessageevent.cpp
@@ -1,44 +1,33 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de>
+// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-FileCopyrightText: 2017 Roman Plášil <me@rplasil.name>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "roommessageevent.h"
#include "logging.h"
+#include "events/eventrelation.h"
#include <QtCore/QFileInfo>
#include <QtCore/QMimeDatabase>
#include <QtGui/QImageReader>
-#include <QtMultimedia/QMediaResource>
+#if QT_VERSION_MAJOR < 6
+# include <QtMultimedia/QMediaResource>
+#endif
using namespace Quotient;
using namespace EventContent;
using MsgType = RoomMessageEvent::MsgType;
-static const auto RelatesToKeyL = "m.relates_to"_ls;
-static const auto MsgTypeKeyL = "msgtype"_ls;
-static const auto FormattedBodyKeyL = "formatted_body"_ls;
-
-static const auto TextTypeKey = "m.text";
-static const auto EmoteTypeKey = "m.emote";
-static const auto NoticeTypeKey = "m.notice";
-
-static const auto HtmlContentTypeId = QStringLiteral("org.matrix.custom.html");
+namespace { // Supporting internal definitions
+constexpr auto RelatesToKey = "m.relates_to"_ls;
+constexpr auto MsgTypeKey = "msgtype"_ls;
+constexpr auto FormattedBodyKey = "formatted_body"_ls;
+constexpr auto TextTypeKey = "m.text"_ls;
+constexpr auto EmoteTypeKey = "m.emote"_ls;
+constexpr auto NoticeTypeKey = "m.notice"_ls;
+constexpr auto HtmlContentTypeId = "org.matrix.custom.html"_ls;
template <typename ContentT>
TypedBase* make(const QJsonObject& json)
@@ -49,13 +38,13 @@ TypedBase* make(const QJsonObject& json)
template <>
TypedBase* make<TextContent>(const QJsonObject& json)
{
- return json.contains(FormattedBodyKeyL) || json.contains(RelatesToKeyL)
+ return json.contains(FormattedBodyKey) || json.contains(RelatesToKey)
? new TextContent(json)
: nullptr;
}
struct MsgTypeDesc {
- QString matrixType;
+ QLatin1String matrixType;
MsgType enumType;
TypedBase* (*maker)(const QJsonObject&);
};
@@ -64,11 +53,11 @@ const std::vector<MsgTypeDesc> msgTypes = {
{ TextTypeKey, MsgType::Text, make<TextContent> },
{ EmoteTypeKey, MsgType::Emote, make<TextContent> },
{ NoticeTypeKey, MsgType::Notice, make<TextContent> },
- { QStringLiteral("m.image"), MsgType::Image, make<ImageContent> },
- { QStringLiteral("m.file"), MsgType::File, make<FileContent> },
- { QStringLiteral("m.location"), MsgType::Location, make<LocationContent> },
- { QStringLiteral("m.video"), MsgType::Video, make<VideoContent> },
- { QStringLiteral("m.audio"), MsgType::Audio, make<AudioContent> }
+ { "m.image"_ls, MsgType::Image, make<ImageContent> },
+ { "m.file"_ls, MsgType::File, make<FileContent> },
+ { "m.location"_ls, MsgType::Location, make<LocationContent> },
+ { "m.video"_ls, MsgType::Video, make<VideoContent> },
+ { "m.audio"_ls, MsgType::Audio, make<AudioContent> }
};
QString msgTypeToJson(MsgType enumType)
@@ -95,49 +84,52 @@ MsgType jsonToMsgType(const QString& matrixType)
return MsgType::Unknown;
}
-inline bool isReplacement(const Omittable<RelatesTo>& rel)
+inline bool isReplacement(const Omittable<EventRelation>& rel)
{
- return rel && rel->type == RelatesTo::ReplacementTypeId();
+ return rel && rel->type == EventRelation::ReplacementType;
}
+} // anonymous namespace
+
QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody,
const QString& jsonMsgType,
TypedBase* content)
{
- auto json = content ? content->toJson() : QJsonObject();
- if (json.contains(RelatesToKeyL)) {
+ QJsonObject json;
+ if (content) {
+ // TODO: replace with content->fillJson(json) when it starts working
+ json = content->toJson();
if (jsonMsgType != TextTypeKey && jsonMsgType != NoticeTypeKey
&& jsonMsgType != EmoteTypeKey) {
- json.remove(RelatesToKeyL);
- qCWarning(EVENTS)
- << RelatesToKeyL << "cannot be used in" << jsonMsgType
- << "messages; the relation has been stripped off";
- } else {
- // After the above, we know for sure that the content is TextContent
- // and that its RelatesTo structure is not omitted
- auto* textContent = static_cast<const TextContent*>(content);
- Q_ASSERT(textContent && textContent->relatesTo.has_value());
- if (textContent->relatesTo->type == RelatesTo::ReplacementTypeId()) {
- auto newContentJson = json.take("m.new_content"_ls).toObject();
- newContentJson.insert(BodyKey, plainBody);
- newContentJson.insert(MsgTypeKeyL, jsonMsgType);
- json.insert(QStringLiteral("m.new_content"), newContentJson);
- json[MsgTypeKeyL] = jsonMsgType;
- json[BodyKeyL] = "* " + plainBody;
- return json;
+ if (json.contains(RelatesToKey)) {
+ json.remove(RelatesToKey);
+ qCWarning(EVENTS)
+ << RelatesToKey << "cannot be used in" << jsonMsgType
+ << "messages; the relation has been stripped off";
}
+ } else if (auto* textContent = static_cast<const TextContent*>(content);
+ textContent->relatesTo
+ && textContent->relatesTo->type
+ == EventRelation::ReplacementType) {
+ auto newContentJson = json.take("m.new_content"_ls).toObject();
+ newContentJson.insert(BodyKey, plainBody);
+ newContentJson.insert(MsgTypeKey, jsonMsgType);
+ json.insert(QStringLiteral("m.new_content"), newContentJson);
+ json[MsgTypeKey] = jsonMsgType;
+ json[BodyKeyL] = "* " + plainBody;
+ return json;
}
}
- json.insert(QStringLiteral("msgtype"), jsonMsgType);
- json.insert(QStringLiteral("body"), plainBody);
+ json.insert(MsgTypeKey, jsonMsgType);
+ json.insert(BodyKey, plainBody);
return json;
}
RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
const QString& jsonMsgType,
TypedBase* content)
- : RoomEvent(typeId(), matrixTypeId(),
- assembleContentJson(plainBody, jsonMsgType, content))
+ : RoomEvent(
+ basicJson(TypeId, assembleContentJson(plainBody, jsonMsgType, content)))
, _content(content)
{}
@@ -146,6 +138,7 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody, MsgType msgType,
: RoomMessageEvent(plainBody, msgTypeToJson(msgType), content)
{}
+#if QT_VERSION_MAJOR < 6
TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile)
{
auto filePath = file.absoluteFilePath();
@@ -179,15 +172,16 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
: rawMsgTypeForFile(file),
contentFromFile(file, asGenericFile))
{}
+#endif
RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj)
- : RoomEvent(typeId(), obj), _content(nullptr)
+ : RoomEvent(obj), _content(nullptr)
{
if (isRedacted())
return;
const QJsonObject content = contentJson();
- if (content.contains(MsgTypeKeyL) && content.contains(BodyKeyL)) {
- auto msgtype = content[MsgTypeKeyL].toString();
+ if (content.contains(MsgTypeKey) && content.contains(BodyKeyL)) {
+ auto msgtype = content[MsgTypeKey].toString();
bool msgTypeFound = false;
for (const auto& mt : msgTypes)
if (mt.matrixType == msgtype) {
@@ -213,12 +207,12 @@ RoomMessageEvent::MsgType RoomMessageEvent::msgtype() const
QString RoomMessageEvent::rawMsgtype() const
{
- return contentJson()[MsgTypeKeyL].toString();
+ return contentPart<QString>(MsgTypeKey);
}
QString RoomMessageEvent::plainBody() const
{
- return contentJson()[BodyKeyL].toString();
+ return contentPart<QString>(BodyKeyL);
}
QMimeType RoomMessageEvent::mimeType() const
@@ -276,7 +270,7 @@ QString RoomMessageEvent::rawMsgTypeForFile(const QFileInfo& fi)
}
TextContent::TextContent(QString text, const QString& contentType,
- Omittable<RelatesTo> relatesTo)
+ Omittable<EventRelation> relatesTo)
: mimeType(QMimeDatabase().mimeTypeForName(contentType))
, body(std::move(text))
, relatesTo(std::move(relatesTo))
@@ -285,26 +279,8 @@ TextContent::TextContent(QString text, const QString& contentType,
mimeType = QMimeDatabase().mimeTypeForName("text/html");
}
-namespace Quotient {
-// Overload the default fromJson<> logic that defined in converters.h
-// as we want
-template <>
-Omittable<RelatesTo> fromJson(const QJsonValue& jv)
-{
- const auto jo = jv.toObject();
- if (jo.isEmpty())
- return none;
- const auto replyJson = jo.value(RelatesTo::ReplyTypeId()).toObject();
- if (!replyJson.isEmpty())
- return replyTo(fromJson<QString>(replyJson[EventIdKeyL]));
-
- return RelatesTo { jo.value("rel_type"_ls).toString(),
- jo.value(EventIdKeyL).toString() };
-}
-} // namespace Quotient
-
TextContent::TextContent(const QJsonObject& json)
- : relatesTo(fromJson<Omittable<RelatesTo>>(json[RelatesToKeyL]))
+ : relatesTo(fromJson<Omittable<EventRelation>>(json[RelatesToKey]))
{
QMimeDatabase db;
static const auto PlainTextMimeType = db.mimeTypeForName("text/plain");
@@ -317,7 +293,7 @@ TextContent::TextContent(const QJsonObject& json)
// of sending HTML messages.
if (actualJson["format"_ls].toString() == HtmlContentTypeId) {
mimeType = HtmlMimeType;
- body = actualJson[FormattedBodyKeyL].toString();
+ body = actualJson[FormattedBodyKey].toString();
} else {
// Falling back to plain text, as there's no standard way to describe
// rich text in messages.
@@ -326,29 +302,30 @@ TextContent::TextContent(const QJsonObject& json)
}
}
-void TextContent::fillJson(QJsonObject* json) const
+void TextContent::fillJson(QJsonObject &json) const
{
static const auto FormatKey = QStringLiteral("format");
- static const auto FormattedBodyKey = QStringLiteral("formatted_body");
- Q_ASSERT(json);
if (mimeType.inherits("text/html")) {
- json->insert(FormatKey, HtmlContentTypeId);
- json->insert(FormattedBodyKey, body);
+ json.insert(FormatKey, HtmlContentTypeId);
+ json.insert(FormattedBodyKey, body);
}
if (relatesTo) {
- json->insert(QStringLiteral("m.relates_to"),
- relatesTo->type == RelatesTo::ReplyTypeId() ?
- QJsonObject { { relatesTo->type, QJsonObject{ { EventIdKey, relatesTo->eventId } } } } :
- QJsonObject { { "rel_type", relatesTo->type }, { EventIdKey, relatesTo->eventId } }
- );
- if (relatesTo->type == RelatesTo::ReplacementTypeId()) {
+ json.insert(
+ QStringLiteral("m.relates_to"),
+ relatesTo->type == EventRelation::ReplyType
+ ? QJsonObject { { relatesTo->type,
+ QJsonObject {
+ { EventIdKey, relatesTo->eventId } } } }
+ : QJsonObject { { RelTypeKey, relatesTo->type },
+ { EventIdKey, relatesTo->eventId } });
+ if (relatesTo->type == EventRelation::ReplacementType) {
QJsonObject newContentJson;
if (mimeType.inherits("text/html")) {
newContentJson.insert(FormatKey, HtmlContentTypeId);
newContentJson.insert(FormattedBodyKey, body);
}
- json->insert(QStringLiteral("m.new_content"), newContentJson);
+ json.insert(QStringLiteral("m.new_content"), newContentJson);
}
}
}
@@ -369,9 +346,8 @@ QMimeType LocationContent::type() const
return QMimeDatabase().mimeTypeForData(geoUri.toLatin1());
}
-void LocationContent::fillJson(QJsonObject* o) const
+void LocationContent::fillJson(QJsonObject& o) const
{
- Q_ASSERT(o);
- o->insert(QStringLiteral("geo_uri"), geoUri);
- o->insert(QStringLiteral("info"), toInfoJson(thumbnail));
+ o.insert(QStringLiteral("geo_uri"), geoUri);
+ o.insert(QStringLiteral("info"), toInfoJson(thumbnail));
}
diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h
index 2501d097..889fc4dc 100644
--- a/lib/events/roommessageevent.h
+++ b/lib/events/roommessageevent.h
@@ -1,24 +1,12 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de>
+// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-FileCopyrightText: 2017 Roman Plášil <me@rplasil.name>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "eventcontent.h"
+#include "eventrelation.h"
#include "roomevent.h"
class QFileInfo;
@@ -29,14 +17,10 @@ namespace MessageEventContent = EventContent; // Back-compatibility
/**
* The event class corresponding to m.room.message events
*/
-class RoomMessageEvent : public RoomEvent {
+class QUOTIENT_API RoomMessageEvent : public RoomEvent {
Q_GADGET
- Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT)
- Q_PROPERTY(QString plainBody READ plainBody CONSTANT)
- Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT)
- Q_PROPERTY(const EventContent::TypedBase* content READ content CONSTANT)
public:
- DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent)
+ QUO_EVENT(RoomMessageEvent, "m.room.message")
enum class MsgType {
Text,
@@ -55,8 +39,12 @@ public:
explicit RoomMessageEvent(const QString& plainBody,
MsgType msgType = MsgType::Text,
EventContent::TypedBase* content = nullptr);
+#if QT_VERSION_MAJOR < 6
+ [[deprecated("Create an EventContent object on the client side"
+ " and pass it to other constructors")]] //
explicit RoomMessageEvent(const QString& plainBody, const QFileInfo& file,
bool asGenericFile = false);
+#endif
explicit RoomMessageEvent(const QJsonObject& obj);
MsgType msgtype() const;
@@ -71,9 +59,26 @@ public:
_content.data());
}
QMimeType mimeType() const;
+ //! \brief Determine whether the message has text content
+ //!
+ //! \return true, if the message type is one of m.text, m.notice, m.emote,
+ //! or the message type is unspecified (in which case plainBody()
+ //! can still be examined); false otherwise
bool hasTextContent() const;
+ //! \brief Determine whether the message has a file/attachment
+ //!
+ //! \return true, if the message has a data structure corresponding to
+ //! a file (such as m.file or m.audio); false otherwise
bool hasFileContent() const;
+ //! \brief Determine whether the message has a thumbnail
+ //!
+ //! \return true, if the message has a data structure corresponding to
+ //! a thumbnail (the message type may be one for visual content,
+ //! such as m.image, or generic binary content, i.e. m.file);
+ //! false otherwise
bool hasThumbnail() const;
+ //! \brief Obtain id of an event replaced by the current one
+ //! \sa RoomEvent::isReplaced, RoomEvent::replacedBy
QString replacedEvent() const;
static QString rawMsgTypeForUrl(const QUrl& url);
@@ -89,47 +94,49 @@ private:
Q_ENUM(MsgType)
};
-REGISTER_EVENT_TYPE(RoomMessageEvent)
+
using MessageEventType = RoomMessageEvent::MsgType;
namespace EventContent {
- // Additional event content types
- struct RelatesTo {
- static constexpr const char* ReplyTypeId() { return "m.in_reply_to"; }
- static constexpr const char* ReplacementTypeId() { return "m.replace"; }
- QString type; // The only supported relation so far
- QString eventId;
+ struct [[deprecated("Use Quotient::EventRelation instead")]] RelatesTo
+ : EventRelation {
+ static constexpr auto ReplyTypeId() { return ReplyType; }
+ static constexpr auto ReplacementTypeId() { return ReplacementType; }
};
- inline RelatesTo replyTo(QString eventId)
+ [[deprecated("Use EventRelation::replyTo() instead")]]
+ inline auto replyTo(QString eventId)
{
- return { RelatesTo::ReplyTypeId(), std::move(eventId) };
+ return EventRelation::replyTo(std::move(eventId));
}
- inline RelatesTo replacementOf(QString eventId)
+ [[deprecated("Use EventRelation::replace() instead")]]
+ inline auto replacementOf(QString eventId)
{
- return { RelatesTo::ReplacementTypeId(), std::move(eventId) };
+ return EventRelation::replace(std::move(eventId));
}
+ // Additional event content types
+
/**
* Rich text content for m.text, m.emote, m.notice
*
* Available fields: mimeType, body. The body can be either rich text
* or plain text, depending on what mimeType specifies.
*/
- class TextContent : public TypedBase {
+ class QUOTIENT_API TextContent : public TypedBase {
public:
TextContent(QString text, const QString& contentType,
- Omittable<RelatesTo> relatesTo = none);
+ Omittable<EventRelation> relatesTo = none);
explicit TextContent(const QJsonObject& json);
QMimeType type() const override { return mimeType; }
QMimeType mimeType;
QString body;
- Omittable<RelatesTo> relatesTo;
+ Omittable<EventRelation> relatesTo;
protected:
- void fillJson(QJsonObject* json) const override;
+ void fillJson(QJsonObject& json) const override;
};
/**
@@ -145,7 +152,7 @@ namespace EventContent {
* - thumbnail.mimeType
* - thumbnail.imageSize
*/
- class LocationContent : public TypedBase {
+ class QUOTIENT_API LocationContent : public TypedBase {
public:
LocationContent(const QString& geoUri, const Thumbnail& thumbnail = {});
explicit LocationContent(const QJsonObject& json);
@@ -157,28 +164,25 @@ namespace EventContent {
Thumbnail thumbnail;
protected:
- void fillJson(QJsonObject* o) const override;
+ void fillJson(QJsonObject& o) const override;
};
/**
* A base class for info types that include duration: audio and video
*/
- template <typename ContentT>
- class PlayableContent : public ContentT {
+ template <typename InfoT>
+ class PlayableContent : public UrlBasedContent<InfoT> {
public:
- using ContentT::ContentT;
+ using UrlBasedContent<InfoT>::UrlBasedContent;
PlayableContent(const QJsonObject& json)
- : ContentT(json)
- , duration(ContentT::originalInfoJson["duration"_ls].toInt())
+ : UrlBasedContent<InfoT>(json)
+ , duration(FileInfo::originalInfoJson["duration"_ls].toInt())
{}
protected:
- void fillJson(QJsonObject* json) const override
+ void fillInfoJson(QJsonObject& infoJson) const override
{
- ContentT::fillJson(json);
- auto infoJson = json->take("info"_ls).toObject();
infoJson.insert(QStringLiteral("duration"), duration);
- json->insert(QStringLiteral("info"), infoJson);
}
public:
@@ -204,7 +208,7 @@ namespace EventContent {
* - mimeType
* - imageSize
*/
- using VideoContent = PlayableContent<UrlWithThumbnailContent<ImageInfo>>;
+ using VideoContent = PlayableContent<ImageInfo>;
/**
* Content class for m.audio
@@ -217,7 +221,13 @@ namespace EventContent {
* - payloadSize ("size" in JSON)
* - mimeType ("mimetype" in JSON)
* - duration
+ * - thumbnail.url ("thumbnail_url" in JSON - extension to the spec)
+ * - corresponding to the "info/thumbnail_info" subobject: contents of
+ * thumbnail field (extension to the spec):
+ * - payloadSize
+ * - mimeType
+ * - imageSize
*/
- using AudioContent = PlayableContent<UrlBasedContent<FileInfo>>;
+ using AudioContent = PlayableContent<FileInfo>;
} // namespace EventContent
} // namespace Quotient
diff --git a/lib/events/roompowerlevelsevent.cpp b/lib/events/roompowerlevelsevent.cpp
index 0a401752..d9bd010b 100644
--- a/lib/events/roompowerlevelsevent.cpp
+++ b/lib/events/roompowerlevelsevent.cpp
@@ -1,9 +1,12 @@
-#include "roompowerlevelsevent.h"
+// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
+// SPDX-License-Identifier: LGPL-2.1-or-later
-#include <QJsonDocument>
+#include "roompowerlevelsevent.h"
using namespace Quotient;
+// The default values used below are defined in
+// https://spec.matrix.org/v1.3/client-server-api/#mroompower_levels
PowerLevelsEventContent::PowerLevelsEventContent(const QJsonObject& json) :
invite(json["invite"_ls].toInt(50)),
kick(json["kick"_ls].toInt(50)),
@@ -15,48 +18,36 @@ PowerLevelsEventContent::PowerLevelsEventContent(const QJsonObject& json) :
users(fromJson<QHash<QString, int>>(json["users"_ls])),
usersDefault(json["users_default"_ls].toInt(0)),
notifications(Notifications{json["notifications"_ls].toObject()["room"_ls].toInt(50)})
-{
-}
+{}
-void PowerLevelsEventContent::fillJson(QJsonObject* o) const {
- o->insert(QStringLiteral("invite"), invite);
- o->insert(QStringLiteral("kick"), kick);
- o->insert(QStringLiteral("ban"), ban);
- o->insert(QStringLiteral("redact"), redact);
- o->insert(QStringLiteral("events"), Quotient::toJson(events));
- o->insert(QStringLiteral("events_default"), eventsDefault);
- o->insert(QStringLiteral("state_default"), stateDefault);
- o->insert(QStringLiteral("users"), Quotient::toJson(users));
- o->insert(QStringLiteral("users_default"), usersDefault);
- o->insert(QStringLiteral("notifications"), QJsonObject{{"room", notifications.room}});
+QJsonObject PowerLevelsEventContent::toJson() const
+{
+ QJsonObject o;
+ o.insert(QStringLiteral("invite"), invite);
+ o.insert(QStringLiteral("kick"), kick);
+ o.insert(QStringLiteral("ban"), ban);
+ o.insert(QStringLiteral("redact"), redact);
+ o.insert(QStringLiteral("events"), Quotient::toJson(events));
+ o.insert(QStringLiteral("events_default"), eventsDefault);
+ o.insert(QStringLiteral("state_default"), stateDefault);
+ o.insert(QStringLiteral("users"), Quotient::toJson(users));
+ o.insert(QStringLiteral("users_default"), usersDefault);
+ o.insert(QStringLiteral("notifications"),
+ QJsonObject { { "room", notifications.room } });
+ return o;
}
-int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) const {
- auto e = events();
-
- if (e.contains(eventId)) {
- return e[eventId];
- }
-
- return eventsDefault();
+int RoomPowerLevelsEvent::powerLevelForEvent(const QString& eventId) const
+{
+ return events().value(eventId, eventsDefault());
}
-int RoomPowerLevelsEvent::powerLevelForState(const QString &eventId) const {
- auto e = events();
-
- if (e.contains(eventId)) {
- return e[eventId];
- }
-
- return stateDefault();
+int RoomPowerLevelsEvent::powerLevelForState(const QString& eventId) const
+{
+ return events().value(eventId, stateDefault());
}
-int RoomPowerLevelsEvent::powerLevelForUser(const QString &userId) const {
- auto u = users();
-
- if (u.contains(userId)) {
- return u[userId];
- }
-
- return usersDefault();
+int RoomPowerLevelsEvent::powerLevelForUser(const QString& userId) const
+{
+ return users().value(userId, usersDefault());
}
diff --git a/lib/events/roompowerlevelsevent.h b/lib/events/roompowerlevelsevent.h
index f0f7207f..6150980a 100644
--- a/lib/events/roompowerlevelsevent.h
+++ b/lib/events/roompowerlevelsevent.h
@@ -1,16 +1,18 @@
+// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
#pragma once
-#include "eventcontent.h"
#include "stateevent.h"
namespace Quotient {
-class PowerLevelsEventContent : public EventContent::Base {
-public:
+struct QUOTIENT_API PowerLevelsEventContent {
struct Notifications {
int room;
};
explicit PowerLevelsEventContent(const QJsonObject& json);
+ QJsonObject toJson() const;
int invite;
int kick;
@@ -26,19 +28,14 @@ public:
int usersDefault;
Notifications notifications;
-
-protected:
- void fillJson(QJsonObject* o) const override;
};
-class RoomPowerLevelsEvent : public StateEvent<PowerLevelsEventContent> {
- Q_GADGET
+class QUOTIENT_API RoomPowerLevelsEvent
+ : public KeylessStateEventBase<RoomPowerLevelsEvent, PowerLevelsEventContent> {
public:
- DEFINE_EVENT_TYPEID("m.room.power_levels", RoomPowerLevelsEvent)
+ QUO_EVENT(RoomPowerLevelsEvent, "m.room.power_levels")
- explicit RoomPowerLevelsEvent(const QJsonObject& obj)
- : StateEvent(typeId(), obj)
- {}
+ using KeylessStateEventBase::KeylessStateEventBase;
int invite() const { return content().invite; }
int kick() const { return content().kick; }
@@ -58,19 +55,5 @@ public:
int powerLevelForEvent(const QString& eventId) const;
int powerLevelForState(const QString& eventId) const;
int powerLevelForUser(const QString& userId) const;
-
-private:
};
-
-template <>
-class EventFactory<RoomPowerLevelsEvent> {
-public:
- static event_ptr_tt<RoomPowerLevelsEvent> make(const QJsonObject& json,
- const QString&)
- {
- return makeEvent<RoomPowerLevelsEvent>(json);
- }
-};
-
-REGISTER_EVENT_TYPE(RoomPowerLevelsEvent)
} // namespace Quotient
diff --git a/lib/events/roomtombstoneevent.cpp b/lib/events/roomtombstoneevent.cpp
index f93eb60d..2c3492d6 100644
--- a/lib/events/roomtombstoneevent.cpp
+++ b/lib/events/roomtombstoneevent.cpp
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2019 QMatrixClient project
- *
- * 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-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "roomtombstoneevent.h"
@@ -22,10 +7,10 @@ using namespace Quotient;
QString RoomTombstoneEvent::serverMessage() const
{
- return fromJson<QString>(contentJson()["body"_ls]);
+ return contentPart<QString>("body"_ls);
}
QString RoomTombstoneEvent::successorRoomId() const
{
- return fromJson<QString>(contentJson()["replacement_room"_ls]);
+ return contentPart<QString>("replacement_room"_ls);
}
diff --git a/lib/events/roomtombstoneevent.h b/lib/events/roomtombstoneevent.h
index 2c2f0663..c85b4dfd 100644
--- a/lib/events/roomtombstoneevent.h
+++ b/lib/events/roomtombstoneevent.h
@@ -1,37 +1,18 @@
-/******************************************************************************
- * Copyright (C) 2019 QMatrixClient project
- *
- * 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-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "stateevent.h"
namespace Quotient {
-class RoomTombstoneEvent : public StateEventBase {
+class QUOTIENT_API RoomTombstoneEvent : public StateEvent {
public:
- DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent)
+ QUO_EVENT(RoomTombstoneEvent, "m.room.tombstone")
- explicit RoomTombstoneEvent() : StateEventBase(typeId(), matrixTypeId()) {}
- explicit RoomTombstoneEvent(const QJsonObject& obj)
- : StateEventBase(typeId(), obj)
- {}
+ using StateEvent::StateEvent;
QString serverMessage() const;
QString successorRoomId() const;
};
-REGISTER_EVENT_TYPE(RoomTombstoneEvent)
} // namespace Quotient
diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h
index cde5b0fd..2a0d3817 100644
--- a/lib/events/simplestateevents.h
+++ b/lib/events/simplestateevents.h
@@ -1,89 +1,47 @@
-/******************************************************************************
- * Copyright (C) 2017 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
- */
+// SPDX-FileCopyrightText: 2017 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "stateevent.h"
+#include "single_key_value.h"
namespace Quotient {
-namespace EventContent {
- template <typename T>
- class SimpleContent {
- public:
- using value_type = T;
-
- // The constructor is templated to enable perfect forwarding
- template <typename TT>
- SimpleContent(QString keyName, TT&& value)
- : value(std::forward<TT>(value)), key(std::move(keyName))
- {}
- SimpleContent(const QJsonObject& json, QString keyName)
- : value(fromJson<T>(json[keyName])), key(std::move(keyName))
- {}
- QJsonObject toJson() const
- {
- return { { key, Quotient::toJson(value) } };
- }
-
- public:
- T value;
-
- protected:
- QString key;
- };
-} // namespace EventContent
-
-#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ValueType, _ContentKey) \
- class _Name : public StateEvent<EventContent::SimpleContent<_ValueType>> { \
- public: \
- using value_type = content_type::value_type; \
- DEFINE_EVENT_TYPEID(_TypeId, _Name) \
- explicit _Name() : _Name(value_type()) {} \
- template <typename T> \
- explicit _Name(T&& value) \
- : StateEvent(typeId(), matrixTypeId(), QString(), \
- QStringLiteral(#_ContentKey), std::forward<T>(value)) \
- {} \
- explicit _Name(QJsonObject obj) \
- : StateEvent(typeId(), std::move(obj), \
- QStringLiteral(#_ContentKey)) \
- {} \
- auto _ContentKey() const { return content().value; } \
- }; \
- REGISTER_EVENT_TYPE(_Name) \
- // End of macro
+#define DEFINE_SIMPLE_STATE_EVENT(Name_, TypeId_, ValueType_, ContentKey_) \
+ constexpr auto Name_##Key = #ContentKey_##_ls; \
+ class QUOTIENT_API Name_ \
+ : public KeylessStateEventBase< \
+ Name_, EventContent::SingleKeyValue<ValueType_, Name_##Key>> { \
+ public: \
+ using value_type = ValueType_; \
+ QUO_EVENT(Name_, TypeId_) \
+ using KeylessStateEventBase::KeylessStateEventBase; \
+ auto ContentKey_() const { return content().value; } \
+ }; \
+// End of macro
DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name)
DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic)
-
-class RoomAliasesEvent
- : public StateEvent<EventContent::SimpleContent<QStringList>> {
+DEFINE_SIMPLE_STATE_EVENT(RoomPinnedEvent, "m.room.pinned_messages",
+ QStringList, pinnedEvents)
+
+constexpr auto RoomAliasesEventKey = "aliases"_ls;
+class QUOTIENT_API RoomAliasesEvent
+ : public KeyedStateEventBase<
+ RoomAliasesEvent,
+ EventContent::SingleKeyValue<QStringList, RoomAliasesEventKey>>
+{
public:
- DEFINE_EVENT_TYPEID("m.room.aliases", RoomAliasesEvent)
- explicit RoomAliasesEvent(const QJsonObject& obj)
- : StateEvent(typeId(), obj, QStringLiteral("aliases"))
- {}
- RoomAliasesEvent(const QString& server, const QStringList& aliases)
- : StateEvent(typeId(), matrixTypeId(), server,
- QStringLiteral("aliases"), aliases)
- {}
+ QUO_EVENT(RoomAliasesEvent, "m.room.aliases")
+ using KeyedStateEventBase::KeyedStateEventBase;
+
+ Q_DECL_DEPRECATED_X(
+ "m.room.aliases events are deprecated by the Matrix spec; use"
+ " RoomCanonicalAliasEvent::altAliases() to get non-authoritative aliases")
QString server() const { return stateKey(); }
+ Q_DECL_DEPRECATED_X(
+ "m.room.aliases events are deprecated by the Matrix spec; use"
+ " RoomCanonicalAliasEvent::altAliases() to get non-authoritative aliases")
QStringList aliases() const { return content().value; }
};
-REGISTER_EVENT_TYPE(RoomAliasesEvent)
} // namespace Quotient
diff --git a/lib/events/single_key_value.h b/lib/events/single_key_value.h
new file mode 100644
index 00000000..ca2bd331
--- /dev/null
+++ b/lib/events/single_key_value.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "converters.h"
+
+namespace Quotient {
+
+namespace EventContent {
+ template <typename T, const QLatin1String& KeyStr>
+ struct SingleKeyValue {
+ // NOLINTBEGIN(google-explicit-constructor): that check should learn
+ // about explicit(false)
+ QUO_IMPLICIT SingleKeyValue(const T& v = {})
+ : value { v }
+ {}
+ QUO_IMPLICIT SingleKeyValue(T&& v)
+ : value { std::move(v) }
+ {}
+ // NOLINTEND(google-explicit-constructor)
+ T value;
+ };
+} // namespace EventContent
+
+template <typename ValueT, const QLatin1String& KeyStr>
+struct JsonConverter<EventContent::SingleKeyValue<ValueT, KeyStr>> {
+ using content_type = EventContent::SingleKeyValue<ValueT, KeyStr>;
+ static content_type load(const QJsonValue& jv)
+ {
+ return { fromJson<ValueT>(jv.toObject().value(JsonKey)) };
+ }
+ static QJsonObject dump(const content_type& c)
+ {
+ return { { JsonKey, toJson(c.value) } };
+ }
+ static inline const auto JsonKey = toSnakeCase(KeyStr);
+};
+} // namespace Quotient
diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp
index 5909e8a6..72ecd5ad 100644
--- a/lib/events/stateevent.cpp
+++ b/lib/events/stateevent.cpp
@@ -1,64 +1,40 @@
-/******************************************************************************
- * Copyright (C) 2018 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
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "stateevent.h"
+#include "logging.h"
using namespace Quotient;
-// Aside from the normal factory to instantiate StateEventBase inheritors
-// StateEventBase itself can be instantiated if there's a state_key JSON key
-// but the event type is unknown.
-[[maybe_unused]] static auto stateEventTypeInitialised =
- RoomEvent::factory_t::addMethod(
- [](const QJsonObject& json, const QString& matrixType) -> StateEventPtr {
- if (!json.contains(StateKeyKeyL))
- return nullptr;
-
- if (auto e = StateEventBase::factory_t::make(json, matrixType))
- return e;
-
- return makeEvent<StateEventBase>(unknownEventTypeId(), json);
- });
+StateEvent::StateEvent(const QJsonObject& json)
+ : RoomEvent(json)
+{
+ Q_ASSERT_X(json.contains(StateKeyKeyL), __FUNCTION__,
+ "Attempt to create a state event without state key");
+}
-StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType,
- const QString& stateKey,
+StateEvent::StateEvent(Event::Type type, const QString& stateKey,
const QJsonObject& contentJson)
- : RoomEvent(type, basicStateEventJson(matrixType, contentJson, stateKey))
+ : RoomEvent(basicJson(type, stateKey, contentJson))
{}
-bool StateEventBase::repeatsState() const
+bool StateEvent::repeatsState() const
{
- const auto prevContentJson = unsignedJson().value(PrevContentKeyL);
- return fullJson().value(ContentKeyL) == prevContentJson;
+ return contentJson() == unsignedPart<QJsonObject>(PrevContentKeyL);
}
-QString StateEventBase::replacedState() const
+QString StateEvent::replacedState() const
{
- return unsignedJson().value("replaces_state"_ls).toString();
+ return unsignedPart<QString>("replaces_state"_ls);
}
-void StateEventBase::dumpTo(QDebug dbg) const
+void StateEvent::dumpTo(QDebug dbg) const
{
if (!stateKey().isEmpty())
dbg << '<' << stateKey() << "> ";
- if (unsignedJson().contains(PrevContentKeyL))
- dbg << QJsonDocument(unsignedJson()[PrevContentKeyL].toObject())
- .toJson(QJsonDocument::Compact)
+ if (const auto prevContentJson = unsignedPart<QJsonObject>(PrevContentKeyL);
+ !prevContentJson.isEmpty())
+ dbg << QJsonDocument(prevContentJson).toJson(QJsonDocument::Compact)
<< " -> ";
RoomEvent::dumpTo(dbg);
}
diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h
index 710b4271..992ec2e2 100644
--- a/lib/events/stateevent.h
+++ b/lib/events/stateevent.h
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2018 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
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
@@ -22,41 +7,54 @@
namespace Quotient {
-/// Make a minimal correct Matrix state event JSON
-template <typename StrT>
-inline QJsonObject basicStateEventJson(StrT matrixType,
- const QJsonObject& content,
- const QString& stateKey = {})
-{
- return { { TypeKey, std::forward<StrT>(matrixType) },
- { StateKeyKey, stateKey },
- { ContentKey, content } };
-}
-
-class StateEventBase : public RoomEvent {
+class QUOTIENT_API StateEvent : public RoomEvent {
public:
- using factory_t = EventFactory<StateEventBase>;
+ QUO_BASE_EVENT(StateEvent, "json.contains('state_key')"_ls,
+ RoomEvent::BaseMetaType)
+ static bool isValid(const QJsonObject& fullJson)
+ {
+ return fullJson.contains(StateKeyKeyL);
+ }
- StateEventBase(Type type, const QJsonObject& json) : RoomEvent(type, json)
- {}
- StateEventBase(Type type, event_mtype_t matrixType,
- const QString& stateKey = {},
- const QJsonObject& contentJson = {});
- ~StateEventBase() override = default;
+ //! \brief Static setting of whether a given even type uses state keys
+ //!
+ //! Most event types don't use a state key; overriding this to `true`
+ //! for a given type changes the calls across Quotient to include state key
+ //! in their signatures; otherwise, state key is still accessible but
+ //! constructors and calls in, e.g., RoomStateView don't include it.
+ static constexpr auto needsStateKey = false;
+
+ explicit StateEvent(Type type, const QString& stateKey = {},
+ const QJsonObject& contentJson = {});
+
+ //! Make a minimal correct Matrix state event JSON
+ static QJsonObject basicJson(const QString& matrixTypeId,
+ const QString& stateKey = {},
+ const QJsonObject& contentJson = {})
+ {
+ return { { TypeKey, matrixTypeId },
+ { StateKeyKey, stateKey },
+ { ContentKey, contentJson } };
+ }
- bool isStateEvent() const override { return true; }
QString replacedState() const;
- void dumpTo(QDebug dbg) const override;
-
virtual bool repeatsState() const;
+
+protected:
+ explicit StateEvent(const QJsonObject& json);
+ void dumpTo(QDebug dbg) const override;
};
-using StateEventPtr = event_ptr_tt<StateEventBase>;
-using StateEvents = EventsArray<StateEventBase>;
+using StateEventBase
+ [[deprecated("StateEventBase is StateEvent now")]] = StateEvent;
+using StateEventPtr = event_ptr_tt<StateEvent>;
+using StateEvents = EventsArray<StateEvent>;
-template <>
-inline bool is<StateEventBase>(const Event& e)
+[[deprecated("Use StateEvent::basicJson() instead")]]
+inline QJsonObject basicStateEventJson(const QString& matrixTypeId,
+ const QJsonObject& content,
+ const QString& stateKey = {})
{
- return e.isStateEvent();
+ return StateEvent::basicJson(matrixTypeId, stateKey, content);
}
/**
@@ -65,67 +63,89 @@ inline bool is<StateEventBase>(const Event& e)
* \sa
* https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events
*/
-using StateEventKey = QPair<QString, QString>;
-
-template <typename ContentT>
-struct Prev {
- template <typename... ContentParamTs>
- explicit Prev(const QJsonObject& unsignedJson,
- ContentParamTs&&... contentParams)
- : senderId(unsignedJson.value("prev_sender"_ls).toString())
- , content(unsignedJson.value(PrevContentKeyL).toObject(),
- std::forward<ContentParamTs>(contentParams)...)
- {}
-
- QString senderId;
- ContentT content;
-};
+using StateEventKey = std::pair<QString, QString>;
-template <typename ContentT>
-class StateEvent : public StateEventBase {
+template <typename EventT, typename ContentT>
+class EventTemplate<EventT, StateEvent, ContentT>
+ : public StateEvent {
public:
using content_type = ContentT;
+ struct Prev {
+ explicit Prev() = default;
+ explicit Prev(const QJsonObject& unsignedJson)
+ : senderId(fromJson<QString>(unsignedJson["prev_sender"_ls]))
+ , content(
+ fromJson<Omittable<ContentT>>(unsignedJson[PrevContentKeyL]))
+ {}
+
+ QString senderId;
+ Omittable<ContentT> content;
+ };
+
+ explicit EventTemplate(const QJsonObject& fullJson)
+ : StateEvent(fullJson)
+ , _content(fromJson<ContentT>(Event::contentJson()))
+ , _prev(unsignedJson())
+ {}
template <typename... ContentParamTs>
- explicit StateEvent(Type type, const QJsonObject& fullJson,
- ContentParamTs&&... contentParams)
- : StateEventBase(type, fullJson)
- , _content(contentJson(), std::forward<ContentParamTs>(contentParams)...)
- {
- const auto& unsignedData = unsignedJson();
- if (unsignedData.contains(PrevContentKeyL))
- _prev = std::make_unique<Prev<ContentT>>(
- unsignedData, std::forward<ContentParamTs>(contentParams)...);
- }
- template <typename... ContentParamTs>
- explicit StateEvent(Type type, event_mtype_t matrixType,
- const QString& stateKey,
- ContentParamTs&&... contentParams)
- : StateEventBase(type, matrixType, stateKey)
- , _content(std::forward<ContentParamTs>(contentParams)...)
+ explicit EventTemplate(const QString& stateKey,
+ ContentParamTs&&... contentParams)
+ : StateEvent(EventT::TypeId, stateKey)
+ , _content { std::forward<ContentParamTs>(contentParams)... }
{
- editJson().insert(ContentKey, _content.toJson());
+ editJson().insert(ContentKey, toJson(_content));
}
const ContentT& content() const { return _content; }
+
template <typename VisitorT>
void editContent(VisitorT&& visitor)
{
visitor(_content);
- editJson()[ContentKeyL] = _content.toJson();
- }
- [[deprecated("Use prevContent instead")]] const ContentT* prev_content() const
- {
- return prevContent();
- }
- const ContentT* prevContent() const
- {
- return _prev ? &_prev->content : nullptr;
+ editJson()[ContentKeyL] = toJson(_content);
}
- QString prevSenderId() const { return _prev ? _prev->senderId : QString(); }
+ const Omittable<ContentT>& prevContent() const { return _prev.content; }
+ QString prevSenderId() const { return _prev.senderId; }
private:
ContentT _content;
- std::unique_ptr<Prev<ContentT>> _prev;
+ Prev _prev;
+};
+
+template <typename EventT, typename ContentT>
+class KeyedStateEventBase
+ : public EventTemplate<EventT, StateEvent, ContentT> {
+public:
+ static constexpr auto needsStateKey = true;
+
+ using EventTemplate<EventT, StateEvent, ContentT>::EventTemplate;
};
+
+template <typename EvT>
+concept Keyed_State_Event = EvT::needsStateKey;
+
+template <typename EventT, typename ContentT>
+class KeylessStateEventBase
+ : public EventTemplate<EventT, StateEvent, ContentT> {
+private:
+ using base_type = EventTemplate<EventT, StateEvent, ContentT>;
+
+public:
+ template <typename... ContentParamTs>
+ explicit KeylessStateEventBase(ContentParamTs&&... contentParams)
+ : base_type(QString(), std::forward<ContentParamTs>(contentParams)...)
+ {}
+
+protected:
+ explicit KeylessStateEventBase(const QJsonObject& fullJson)
+ : base_type(fullJson)
+ {}
+};
+
+template <typename EvT>
+concept Keyless_State_Event = !EvT::needsStateKey;
+
} // namespace Quotient
+Q_DECLARE_METATYPE(Quotient::StateEvent*)
+Q_DECLARE_METATYPE(const Quotient::StateEvent*)
diff --git a/lib/events/stickerevent.h b/lib/events/stickerevent.h
new file mode 100644
index 00000000..67905481
--- /dev/null
+++ b/lib/events/stickerevent.h
@@ -0,0 +1,48 @@
+// SDPX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+
+#include "roomevent.h"
+#include "eventcontent.h"
+
+namespace Quotient {
+
+/// Sticker messages are specialised image messages that are displayed without
+/// controls (e.g. no "download" link, or light-box view on click, as would be
+/// displayed for for m.image events).
+class QUOTIENT_API StickerEvent : public RoomEvent
+{
+public:
+ QUO_EVENT(StickerEvent, "m.sticker")
+
+ explicit StickerEvent(const QJsonObject& obj)
+ : RoomEvent(TypeId, obj)
+ , m_imageContent(
+ EventContent::ImageContent(obj["content"_ls].toObject()))
+ {}
+
+ /// \brief A textual representation or associated description of the
+ /// sticker image.
+ ///
+ /// This could be the alt text of the original image, or a message to
+ /// accompany and further describe the sticker.
+ QUO_CONTENT_GETTER(QString, body)
+
+ /// \brief Metadata about the image referred to in url including a
+ /// thumbnail representation.
+ const EventContent::ImageContent& image() const
+ {
+ return m_imageContent;
+ }
+
+ /// \brief The URL to the sticker image. This must be a valid mxc:// URI.
+ QUrl url() const
+ {
+ return m_imageContent.url();
+ }
+
+private:
+ EventContent::ImageContent m_imageContent;
+};
+} // namespace Quotient
diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp
deleted file mode 100644
index a95d2f0d..00000000
--- a/lib/events/typingevent.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * 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
- */
-
-#include "typingevent.h"
-
-#include <QtCore/QJsonArray>
-
-using namespace Quotient;
-
-TypingEvent::TypingEvent(const QJsonObject& obj) : Event(typeId(), obj)
-{
- const auto& array = contentJson()["user_ids"_ls].toArray();
- _users.reserve(array.size());
- for (const auto& user : array)
- _users.push_back(user.toString());
-}
diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h
index 1cf4e69d..b56475af 100644
--- a/lib/events/typingevent.h
+++ b/lib/events/typingevent.h
@@ -1,36 +1,10 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * 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-FileCopyrightText: 2017 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "event.h"
namespace Quotient {
-class TypingEvent : public Event {
-public:
- DEFINE_EVENT_TYPEID("m.typing", TypingEvent)
-
- TypingEvent(const QJsonObject& obj);
-
- const QStringList& users() const { return _users; }
-
-private:
- QStringList _users;
-};
-REGISTER_EVENT_TYPE(TypingEvent)
+DEFINE_SIMPLE_EVENT(TypingEvent, Event, "m.typing", QStringList, users, "user_ids")
} // namespace Quotient