diff options
author | Andres Salomon <dilinger@queued.net> | 2021-01-18 04:00:14 -0500 |
---|---|---|
committer | Andres Salomon <dilinger@queued.net> | 2021-01-18 04:00:14 -0500 |
commit | 09eb39236666e81d5da014acea011dcd74d0999b (patch) | |
tree | 52876d96be71be1a39d5d935c1295a51995e8949 /lib/events | |
parent | f1788ee27f33e9339334e0d79bde9a27d9ce2e44 (diff) | |
parent | a4e78956f105875625b572d8b98459ffa86fafe5 (diff) | |
download | libquotient-09eb39236666e81d5da014acea011dcd74d0999b.tar.gz libquotient-09eb39236666e81d5da014acea011dcd74d0999b.zip |
Update upstream source from tag 'upstream/0.6.4'
Update to upstream version '0.6.4'
with Debian dir aa8705fd74743e79c043bc9e3e425d5064404cfe
Diffstat (limited to 'lib/events')
46 files changed, 2239 insertions, 1781 deletions
diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index a43e358c..a55016d9 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -1,5 +1,3 @@ -#include <utility> - /****************************************************************************** * Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net> * @@ -15,87 +13,80 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once +#include "converters.h" #include "event.h" #include "eventcontent.h" -#include "converters.h" -namespace QMatrixClient -{ - constexpr const char* FavouriteTag = "m.favourite"; - constexpr const char* LowPriorityTag = "m.lowpriority"; - constexpr const char* ServerNoticeTag = "m.server_notice"; +namespace Quotient { +constexpr const char* FavouriteTag = "m.favourite"; +constexpr const char* LowPriorityTag = "m.lowpriority"; +constexpr const char* ServerNoticeTag = "m.server_notice"; - struct TagRecord - { - using order_type = Omittable<float>; +struct TagRecord { + using order_type = Omittable<float>; - order_type order; + order_type order; - TagRecord (order_type order = none) : order(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 - return !order.omitted() && - (other.order.omitted() || order.value() < other.order.value()); - } - }; + 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); + } +}; - template <> struct JsonObjectConverter<TagRecord> +template <> +struct JsonObjectConverter<TagRecord> { + static void fillFrom(const QJsonObject& jo, TagRecord& rec) { - static void fillFrom(const QJsonObject& jo, TagRecord& rec) - { - // Parse a float both from JSON double and JSON string because - // libqmatrixclient previously used to use strings to store order. - const auto orderJv = jo.value("order"_ls); - if (orderJv.isDouble()) - rec.order = fromJson<float>(orderJv); - if (orderJv.isString()) - { - bool ok; - rec.order = orderJv.toString().toFloat(&ok); - if (!ok) - rec.order = none; - } - } - static void dumpTo(QJsonObject& jo, const TagRecord& rec) - { - addParam<IfNotEmpty>(jo, QStringLiteral("order"), rec.order); + // Parse a float both from JSON double and JSON string because + // the library previously used to use strings to store order. + const auto orderJv = jo.value("order"_ls); + if (orderJv.isDouble()) + rec.order = fromJson<float>(orderJv); + if (orderJv.isString()) { + bool ok; + rec.order = orderJv.toString().toFloat(&ok); + if (!ok) + rec.order = none; } - }; + } + static void dumpTo(QJsonObject& jo, const TagRecord& rec) + { + addParam<IfNotEmpty>(jo, QStringLiteral("order"), rec.order); + } +}; - using TagsMap = QHash<QString, 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 fromJson<content_type>(contentJson()[#_ContentKey##_ls]); } \ - }; \ - REGISTER_EVENT_TYPE(_Name) \ +#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_EVENTTYPE_ALIAS(Tag, TagEvent) - DEFINE_EVENTTYPE_ALIAS(ReadMarker, ReadMarkerEvent) -} +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) +} // namespace Quotient diff --git a/lib/events/callanswerevent.cpp b/lib/events/callanswerevent.cpp index d2862241..d6622b30 100644 --- a/lib/events/callanswerevent.cpp +++ b/lib/events/callanswerevent.cpp @@ -13,13 +13,12 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "callanswerevent.h" #include "event.h" - #include "logging.h" #include <QtCore/QJsonDocument> @@ -45,7 +44,7 @@ m.call.answer } */ -using namespace QMatrixClient; +using namespace Quotient; CallAnswerEvent::CallAnswerEvent(const QJsonObject& obj) : CallEventBase(typeId(), obj) @@ -55,18 +54,18 @@ CallAnswerEvent::CallAnswerEvent(const QJsonObject& obj) 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 } } } - }) -{ } + : 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 } } } - }) -{ } + : 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 index 2d9e5bb0..2709882b 100644 --- a/lib/events/callanswerevent.h +++ b/lib/events/callanswerevent.h @@ -13,33 +13,33 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class CallAnswerEvent: public CallEventBase - { - public: - DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent) +namespace Quotient { +class CallAnswerEvent : public CallEventBase { +public: + DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent) - explicit CallAnswerEvent(const QJsonObject& obj); + explicit CallAnswerEvent(const QJsonObject& obj); - explicit CallAnswerEvent(const QString& callId, const int lifetime, - const QString& sdp); - explicit CallAnswerEvent(const QString& callId, const QString& sdp); + 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(); - } - }; + 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) - DEFINE_EVENTTYPE_ALIAS(CallAnswer, CallAnswerEvent) -} // namespace QMatrixClient +REGISTER_EVENT_TYPE(CallAnswerEvent) +} // namespace Quotient diff --git a/lib/events/callcandidatesevent.cpp b/lib/events/callcandidatesevent.cpp index 52cd1856..24f0dd46 100644 --- a/lib/events/callcandidatesevent.cpp +++ b/lib/events/callcandidatesevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "callcandidatesevent.h" @@ -26,9 +26,8 @@ m.call.candidates "call_id": "12345", "candidates": [ { - "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0", - "sdpMLineIndex": 0, - "sdpMid": "audio" + "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 +43670 typ host generation 0", "sdpMLineIndex": 0, "sdpMid": "audio" } ], "version": 0 diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h index 4618832c..e224f048 100644 --- a/lib/events/callcandidatesevent.h +++ b/lib/events/callcandidatesevent.h @@ -13,36 +13,33 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class CallCandidatesEvent: public CallEventBase - { - public: - DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent) +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 QJsonObject& obj) + : CallEventBase(typeId(), obj) + {} - explicit CallCandidatesEvent(const QString& callId, - const QJsonArray& candidates) - : CallEventBase(typeId(), matrixTypeId(), callId, 0, - {{ QStringLiteral("candidates"), candidates }}) - { } + explicit CallCandidatesEvent(const QString& callId, + const QJsonArray& candidates) + : CallEventBase(typeId(), matrixTypeId(), callId, 0, + { { QStringLiteral("candidates"), candidates } }) + {} - QJsonArray candidates() const - { - return content<QJsonArray>("candidates"_ls); - } - }; + QJsonArray candidates() const + { + return content<QJsonArray>("candidates"_ls); + } +}; - REGISTER_EVENT_TYPE(CallCandidatesEvent) - DEFINE_EVENTTYPE_ALIAS(CallCandidates, CallCandidatesEvent) -} +REGISTER_EVENT_TYPE(CallCandidatesEvent) +} // namespace Quotient diff --git a/lib/events/callhangupevent.cpp b/lib/events/callhangupevent.cpp index b1154806..d41849c3 100644 --- a/lib/events/callhangupevent.cpp +++ b/lib/events/callhangupevent.cpp @@ -13,13 +13,12 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "callhangupevent.h" #include "event.h" - #include "logging.h" #include <QtCore/QJsonDocument> @@ -40,8 +39,7 @@ m.call.hangup } */ -using namespace QMatrixClient; - +using namespace Quotient; CallHangupEvent::CallHangupEvent(const QJsonObject& obj) : CallEventBase(typeId(), obj) @@ -51,4 +49,4 @@ CallHangupEvent::CallHangupEvent(const QJsonObject& obj) CallHangupEvent::CallHangupEvent(const QString& callId) : CallEventBase(typeId(), matrixTypeId(), callId, 0) -{ } +{} diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h index c74e20d5..5d73fb62 100644 --- a/lib/events/callhangupevent.h +++ b/lib/events/callhangupevent.h @@ -13,24 +13,21 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class CallHangupEvent: public CallEventBase - { - public: - DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent) - - explicit CallHangupEvent(const QJsonObject& obj); - explicit CallHangupEvent(const QString& callId); - }; +namespace Quotient { +class CallHangupEvent : public CallEventBase { +public: + DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent) - REGISTER_EVENT_TYPE(CallHangupEvent) - DEFINE_EVENTTYPE_ALIAS(CallHangup, 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 index bca3f296..54faac8d 100644 --- a/lib/events/callinviteevent.cpp +++ b/lib/events/callinviteevent.cpp @@ -13,13 +13,12 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "callinviteevent.h" #include "event.h" - #include "logging.h" #include <QtCore/QJsonDocument> @@ -45,7 +44,7 @@ m.call.invite } */ -using namespace QMatrixClient; +using namespace Quotient; CallInviteEvent::CallInviteEvent(const QJsonObject& obj) : CallEventBase(typeId(), obj) @@ -55,10 +54,10 @@ CallInviteEvent::CallInviteEvent(const QJsonObject& obj) 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 } } - }}) -{ } + : 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 index d5315309..b067a492 100644 --- a/lib/events/callinviteevent.h +++ b/lib/events/callinviteevent.h @@ -13,32 +13,32 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class CallInviteEvent: public CallEventBase - { - public: - DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent) - - explicit CallInviteEvent(const QJsonObject& obj); +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); + 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(); - } - }; + 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) - DEFINE_EVENTTYPE_ALIAS(CallInvite, CallInviteEvent) -} +REGISTER_EVENT_TYPE(CallInviteEvent) +} // namespace Quotient diff --git a/lib/events/directchatevent.cpp b/lib/events/directchatevent.cpp index 266d60d8..b4027e16 100644 --- a/lib/events/directchatevent.cpp +++ b/lib/events/directchatevent.cpp @@ -13,26 +13,25 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "directchatevent.h" #include <QtCore/QJsonArray> -using namespace QMatrixClient; +using namespace Quotient; QMultiHash<QString, QString> DirectChatEvent::usersToDirectChats() const { QMultiHash<QString, QString> result; const auto& json = contentJson(); - for (auto it = json.begin(); it != json.end(); ++it) - { + for (auto it = json.begin(); it != json.end(); ++it) { // Beware of range-for's over temporary returned from temporary // (see the bottom of // http://en.cppreference.com/w/cpp/language/range-for#Explanation) const auto roomIds = it.value().toArray(); - for (const auto& roomIdValue: roomIds) + for (const auto& roomIdValue : roomIds) result.insert(it.key(), roomIdValue.toString()); } return result; diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index 7559796b..bb091c5c 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -13,26 +13,21 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "event.h" -namespace QMatrixClient -{ - class DirectChatEvent : public Event - { - public: - DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) +namespace Quotient { +class DirectChatEvent : public Event { +public: + DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) - explicit DirectChatEvent(const QJsonObject& obj) - : Event(typeId(), obj) - { } + explicit DirectChatEvent(const QJsonObject& obj) : Event(typeId(), obj) {} - QMultiHash<QString, QString> usersToDirectChats() const; - }; - REGISTER_EVENT_TYPE(DirectChatEvent) - DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent) -} + QMultiHash<QString, QString> usersToDirectChats() const; +}; +REGISTER_EVENT_TYPE(DirectChatEvent) +} // namespace Quotient diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp new file mode 100644 index 00000000..dccfa540 --- /dev/null +++ b/lib/events/encryptedevent.cpp @@ -0,0 +1,32 @@ +#include "encryptedevent.h" + +#include "room.h" + +using namespace Quotient; +using namespace QtOlm; + +EncryptedEvent::EncryptedEvent(const QJsonObject& ciphertext, + const QString& senderKey) + : RoomEvent(typeId(), matrixTypeId(), + { { AlgorithmKeyL, OlmV1Curve25519AesSha2AlgoKey }, + { CiphertextKeyL, ciphertext }, + { SenderKeyKeyL, senderKey } }) +{} + +EncryptedEvent::EncryptedEvent(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 }, + }) +{} + +EncryptedEvent::EncryptedEvent(const QJsonObject& obj) + : RoomEvent(typeId(), obj) +{ + qCDebug(E2EE) << "Encrypted event from" << senderId(); +} diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h new file mode 100644 index 00000000..235b2aa4 --- /dev/null +++ b/lib/events/encryptedevent.h @@ -0,0 +1,66 @@ +#pragma once + +#include "e2ee.h" +#include "roomevent.h" + +namespace Quotient { +class Room; +/* + * While the specification states: + * + * "This event type is used when sending encrypted events. + * It can be used either within a room + * (in which case it will have all of the Room Event fields), + * or as a to-device event." + * "The encrypted payload can contain any message event." + * https://matrix.org/docs/spec/client_server/latest#id493 + * + * -- for most of the cases the message event is the room message event. + * And even for the to-device events the context is for the room. + * + * So, to simplify integration to the timeline, EncryptedEvent is a RoomEvent + * inheritor. Strictly speaking though, it's not always a RoomEvent, but an Event + * 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 +public: + DEFINE_EVENT_TYPEID("m.room.encrypted", EncryptedEvent) + + /* 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, + 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 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; + } + QByteArray ciphertext() const + { + return content<QString>(CiphertextKeyL).toLatin1(); + } + QJsonObject ciphertext(const QString& identityKey) const + { + return content<QJsonObject>(CiphertextKeyL).value(identityKey).toObject(); + } + QString senderKey() const { return content<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) + +} // namespace Quotient diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp new file mode 100644 index 00000000..f1bde621 --- /dev/null +++ b/lib/events/encryptionevent.cpp @@ -0,0 +1,45 @@ +#include "encryptionevent.h" + +#include "e2ee.h" + +#include <array> + +namespace Quotient { +static const std::array<QString, 1> 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; + +EncryptionEventContent::EncryptionEventContent(const QJsonObject& json) + : encryption(fromJson<EncryptionType>(json[AlgorithmKeyL])) + , algorithm(sanitized(json[AlgorithmKeyL].toString())) + , rotationPeriodMs(json[RotationPeriodMsKeyL].toInt(604800000)) + , rotationPeriodMsgs(json[RotationPeriodMsgsKeyL].toInt(100)) +{} + +void EncryptionEventContent::fillJson(QJsonObject* o) const +{ + Q_ASSERT(o); + if (encryption != EncryptionType::Undefined) + o->insert(AlgorithmKey, algorithm); + o->insert(RotationPeriodMsKey, rotationPeriodMs); + o->insert(RotationPeriodMsgsKey, rotationPeriodMsgs); +} diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h new file mode 100644 index 00000000..cbd3ba4a --- /dev/null +++ b/lib/events/encryptionevent.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * 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 + */ + +#pragma once + +#include "eventcontent.h" +#include "stateevent.h" + +namespace Quotient { +class EncryptionEventContent : public EventContent::Base { +public: + enum EncryptionType : size_t { MegolmV1AesSha2 = 0, Undefined }; + + explicit EncryptionEventContent(EncryptionType et = Undefined) + : encryption(et) + {} + explicit EncryptionEventContent(const QJsonObject& json); + + EncryptionType encryption; + QString algorithm; + int rotationPeriodMs; + int rotationPeriodMsgs; + +protected: + void fillJson(QJsonObject* o) const override; +}; + +using EncryptionType = EncryptionEventContent::EncryptionType; + +class EncryptionEvent : public StateEvent<EncryptionEventContent> { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.room.encryption", EncryptionEvent) + + using EncryptionType = EncryptionEventContent::EncryptionType; + + 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)...) + {} + + 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) +}; + +REGISTER_EVENT_TYPE(EncryptionEvent) +} // namespace Quotient diff --git a/lib/events/event.cpp b/lib/events/event.cpp index 6505d89a..7b34114d 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "event.h" @@ -22,7 +22,7 @@ #include <QtCore/QJsonDocument> -using namespace QMatrixClient; +using namespace Quotient; event_type_t EventTypeRegistry::initializeTypeId(event_mtype_t matrixTypeId) { @@ -31,23 +31,21 @@ event_type_t EventTypeRegistry::initializeTypeId(event_mtype_t 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; + qDebug(EVENTS) << "Initialized event type" << matrixTypeId << "with id" + << id; return id; } QString EventTypeRegistry::getMatrixType(event_type_t typeId) { - return typeId < get().eventTypes.size() - ? get().eventTypes[typeId] : QString(); + return typeId < get().eventTypes.size() ? get().eventTypes[typeId] + : QString(); } -Event::Event(Type type, const QJsonObject& json) - : _type(type), _json(json) +Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json) { - if (!json.contains(ContentKeyL) && - !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) - { + if (!json.contains(ContentKeyL) + && !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) { qCWarning(EVENTS) << "Event without 'content' node"; qCWarning(EVENTS) << formatJson << json; } @@ -55,25 +53,22 @@ Event::Event(Type type, const QJsonObject& 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(); -} +QString Event::matrixType() const { return fullJson()[TypeKeyL].toString(); } -QByteArray Event::originalJson() const -{ - return QJsonDocument(_json).toJson(); -} +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(); diff --git a/lib/events/event.h b/lib/events/event.h index b7bbd83e..6c8961ad 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -22,393 +22,372 @@ #include "logging.h" #ifdef ENABLE_EVENTTYPE_ALIAS -#define USE_EVENTTYPE_ALIAS 1 +# define USE_EVENTTYPE_ALIAS 1 #endif -namespace QMatrixClient +namespace Quotient { +// === event_ptr_tt<> and type casting facilities === + +template <typename EventT> +using event_ptr_tt = std::unique_ptr<EventT>; + +/// Unwrap a plain pointer from a smart pointer +template <typename EventT> +inline EventT* rawPtr(const event_ptr_tt<EventT>& ptr) { - // === event_ptr_tt<> and type casting facilities === + return ptr.get(); +} - template <typename EventT> - using event_ptr_tt = std::unique_ptr<EventT>; +/// Unwrap a plain pointer and downcast it to the specified type +template <typename TargetEventT, typename EventT> +inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr) +{ + return static_cast<TargetEventT*>(rawPtr(ptr)); +} - /// Unwrap a plain pointer from a smart pointer - template <typename EventT> - inline EventT* rawPtr(const event_ptr_tt<EventT>& ptr) - { - return ptr.get(); - } +/// 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 } }; +} - /// Unwrap a plain pointer and downcast it to the specified type - template <typename TargetEventT, typename EventT> - inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr) - { - return static_cast<TargetEventT*>(rawPtr(ptr)); - } +// === Event types and event types registry === - /// 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 unique_ptr_cast<TargetT>(ptr); - } +using event_type_t = size_t; +using event_mtype_t = const char*; + +class EventTypeRegistry { +public: + ~EventTypeRegistry() = default; - // === Standard Matrix key names and basicEventJson() === - - static const auto TypeKey = QStringLiteral("type"); - static const auto ContentKey = QStringLiteral("content"); - static const auto EventIdKey = QStringLiteral("event_id"); - static const auto UnsignedKey = QStringLiteral("unsigned"); - static const auto TypeKeyL = "type"_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; - - // Minimal correct Matrix event JSON - template <typename StrT> - inline QJsonObject basicEventJson(StrT matrixType, - const QJsonObject& content) + static event_type_t initializeTypeId(event_mtype_t matrixTypeId); + + template <typename EventT> + static inline event_type_t initializeTypeId() { - return { { TypeKey, std::forward<StrT>(matrixType) }, - { ContentKey, content } }; + return initializeTypeId(EventT::matrixTypeId()); } - // === Event types and event types registry === + static QString getMatrixType(event_type_t typeId); - using event_type_t = size_t; - using event_mtype_t = const char*; +private: + EventTypeRegistry() = default; + Q_DISABLE_COPY(EventTypeRegistry) + DISABLE_MOVE(EventTypeRegistry) - class EventTypeRegistry + static EventTypeRegistry& get() { - public: - ~EventTypeRegistry() = default; + static EventTypeRegistry etr; + return etr; + } - static event_type_t initializeTypeId(event_mtype_t matrixTypeId); + std::vector<event_mtype_t> eventTypes; +}; - template <typename EventT> - static inline event_type_t initializeTypeId() - { - return initializeTypeId(EventT::matrixTypeId()); - } +template <> +inline event_type_t EventTypeRegistry::initializeTypeId<void>() +{ + return initializeTypeId(""); +} - static QString getMatrixType(event_type_t typeId); +template <typename EventT> +struct EventTypeTraits { + static event_type_t id() + { + static const auto id = EventTypeRegistry::initializeTypeId<EventT>(); + return id; + } +}; - private: - EventTypeRegistry() = default; - Q_DISABLE_COPY(EventTypeRegistry) - DISABLE_MOVE(EventTypeRegistry) +template <typename EventT> +inline event_type_t typeId() +{ + return EventTypeTraits<std::decay_t<EventT>>::id(); +} - static EventTypeRegistry& get() - { - static EventTypeRegistry etr; - return etr; - } +inline event_type_t unknownEventTypeId() { return typeId<void>(); } - std::vector<event_mtype_t> eventTypes; - }; +// === EventFactory === - template <> - inline event_type_t EventTypeRegistry::initializeTypeId<void>() +/** Create an event of arbitrary type from its arguments */ +template <typename EventT, typename... ArgTs> +inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args) +{ + return std::make_unique<EventT>(std::forward<ArgTs>(args)...); +} + +template <typename BaseEventT> +class EventFactory { +public: + template <typename FnT> + static auto addMethod(FnT&& method) { - return initializeTypeId(""); + 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> - struct EventTypeTraits + static auto chainFactory() { - static event_type_t id() - { - static const auto id = EventTypeRegistry::initializeTypeId<EventT>(); - return id; - } - }; + return addMethod(&EventT::factory_t::make); + } - template <typename EventT> - inline event_type_t typeId() + static event_ptr_tt<BaseEventT> make(const QJsonObject& json, + const QString& matrixType) { - return EventTypeTraits<std::decay_t<EventT>>::id(); + for (const auto& f : factories()) + if (auto e = f(json, matrixType)) + return e; + return nullptr; } - inline event_type_t unknownEventTypeId() { return typeId<void>(); } +private: + static auto& factories() + { + using inner_factory_tt = std::function<event_ptr_tt<BaseEventT>( + const QJsonObject&, const QString&)>; + static std::vector<inner_factory_tt> _factories {}; + return _factories; + } +}; - // === EventFactory === +/** 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() +{ + 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; + }); +} + +template <typename EventT> +inline auto registerEventType() +{ + // 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 +} + +// === Event === + +class Event { + Q_GADGET + Q_PROPERTY(Type type READ type CONSTANT) + Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) +public: + using Type = event_type_t; + using factory_t = EventFactory<Event>; + + 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& operator=(Event&&) = delete; + virtual ~Event(); + + Type type() const { return _type; } + QString matrixType() const; + QByteArray originalJson() const; + QJsonObject originalJsonObject() const { return fullJson(); } + + const QJsonObject& fullJson() const { return _json; } + + // According to the CS API spec, every event also has + // a "content" object; but since its structure is different for + // different types, we're implementing it per-event type. + + const QJsonObject contentJson() const; + const QJsonObject unsignedJson() const; - /** Create an event of arbitrary type from its arguments */ - template <typename EventT, typename... ArgTs> - inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args) + template <typename T> + T content(const QString& key) const { - return std::make_unique<EventT>(std::forward<ArgTs>(args)...); + return fromJson<T>(contentJson()[key]); } - template <typename BaseEventT> - class EventFactory - { - 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() - { - 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; - } - - private: - static auto& factories() - { - using inner_factory_tt = - std::function<event_ptr_tt<BaseEventT>(const QJsonObject&, - const QString&)>; - static std::vector<inner_factory_tt> _factories {}; - return _factories; - } - }; - - /** 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() + template <typename T> + T content(QLatin1String key) const { - 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 fromJson<T>(contentJson()[key]); } - template <typename EventT> - inline auto registerEventType() + friend QDebug operator<<(QDebug dbg, const Event& e) { - // 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 + QDebugStateSaver _dss { dbg }; + dbg.noquote().nospace() << e.matrixType() << '(' << e.type() << "): "; + e.dumpTo(dbg); + return dbg; } - // === Event === + virtual bool isStateEvent() const { return false; } + virtual bool isCallEvent() const { return false; } + virtual void dumpTo(QDebug dbg) const; - class Event - { - Q_GADGET - Q_PROPERTY(Type type READ type CONSTANT) - Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) - public: - using Type = event_type_t; - using factory_t = EventFactory<Event>; - - 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& operator=(Event&&) = delete; - virtual ~Event(); - - Type type() const { return _type; } - QString matrixType() const; - QByteArray originalJson() const; - QJsonObject originalJsonObject() const { return fullJson(); } - - const QJsonObject& fullJson() const { return _json; } - - // According to the CS API spec, every event also has - // a "content" object; but since its structure is different for - // different types, we're implementing it per-event type. - - const QJsonObject contentJson() const; - const QJsonObject unsignedJson() const; - - template <typename T> - T content(const QString& key) const - { - return fromJson<T>(contentJson()[key]); - } - - template <typename T> - T content(const QLatin1String& key) const - { - return fromJson<T>(contentJson()[key]); - } - - friend QDebug operator<<(QDebug dbg, const Event& e) - { - QDebugStateSaver _dss { dbg }; - dbg.noquote().nospace() - << e.matrixType() << '(' << e.type() << "): "; - e.dumpTo(dbg); - return dbg; - } - - virtual bool isStateEvent() const { return false; } - virtual bool isCallEvent() const { return false; } - virtual void dumpTo(QDebug dbg) const; - - protected: - QJsonObject& editJson() { return _json; } - - private: - Type _type; - QJsonObject _json; - }; - using EventPtr = event_ptr_tt<Event>; +protected: + QJsonObject& editJson() { return _json; } - template <typename EventT> - using EventsArray = std::vector<event_ptr_tt<EventT>>; - using Events = EventsArray<Event>; +private: + Type _type; + QJsonObject _json; +}; +using EventPtr = event_ptr_tt<Event>; - // === Macros used with event class definitions === +template <typename EventT> +using EventsArray = std::vector<event_ptr_tt<EventT>>; +using Events = EventsArray<Event>; - // 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 QMatrixClient::typeId<_Type>(); } \ - // End of macro +// === Macros used with event class definitions === - // 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 { \ - [[gnu::unused]] \ - static const auto _factoryAdded##_Type = registerEventType<_Type>(); \ - } \ +// 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>(); } \ // End of macro -#ifdef USE_EVENTTYPE_ALIAS - namespace EventType - { - inline event_type_t logEventType(event_type_t id, const char* idName) - { - qDebug(EVENTS) << "Using id" << id << "for" << idName; - return id; - } - } - - // This macro provides constants in EventType:: namespace for - // back-compatibility with libQMatrixClient 0.3 event type system. -#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \ - namespace EventType \ - { \ - [[deprecated("Use is<>(), eventCast<>() or visit<>()")]] \ - static const auto _Id = logEventType(typeId<_Type>(), #_Id); \ - } \ +// 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>(); \ + } \ // End of macro -#else -#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) // Nothing -#endif - - // === is<>(), eventCast<>() and visit<>() === - template <typename EventT> - inline bool is(const Event& e) { return e.type() == typeId<EventT>(); } +// === is<>(), eventCast<>() and visit<>() === - inline bool isUnknown(const Event& e) { return e.type() == unknownEventTypeId(); } - - template <typename 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; - } +template <typename EventT> +inline bool is(const Event& e) +{ + return e.type() == typeId<EventT>(); +} - // A single generic catch-all visitor - template <typename BaseEventT, typename FnT> - inline auto visit(const BaseEventT& event, FnT&& visitor) - -> decltype(visitor(event)) - { - return visitor(event); - } +inline bool isUnknown(const Event& e) +{ + return e.type() == unknownEventTypeId(); +} - template <typename T> - constexpr auto is_event() - { - return std::is_base_of<Event, std::decay_t<T>>::value; - } +template <typename 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; +} + +// A single generic catch-all visitor +template <typename BaseEventT, typename FnT> +inline auto visit(const BaseEventT& event, FnT&& visitor) + -> decltype(visitor(event)) +{ + return visitor(event); +} +namespace _impl { template <typename T, typename FnT> - constexpr auto needs_cast() - { - return !std::is_convertible<T, fn_arg_t<FnT>>::value; - } - - // A single type-specific void visitor - template <typename BaseEventT, typename FnT> - inline - std::enable_if_t< - is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>() && - std::is_void<fn_return_t<FnT>>::value> - visit(const BaseEventT& event, FnT&& visitor) + constexpr auto needs_downcast() { - using event_type = fn_arg_t<FnT>; - if (is<std::decay_t<event_type>>(event)) - visitor(static_cast<event_type>(event)); + return !std::is_convertible_v<T, fn_arg_t<FnT>>; } +} - // A single type-specific non-void visitor with an optional default value - template <typename BaseEventT, typename FnT> - inline - std::enable_if_t< - is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>(), - fn_return_t<FnT>> // non-voidness is guarded by defaultValue type - visit(const BaseEventT& event, FnT&& visitor, - fn_return_t<FnT>&& defaultValue = {}) - { - 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); - } - - // A chain of 2 or more visitors - template <typename BaseEventT, typename FnT1, typename FnT2, typename... FnTs> - inline - std::enable_if_t<is_event<BaseEventT>(), fn_return_t<FnT1>> - visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2, - FnTs&&... visitors) - { - 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)...); - } -} // namespace QMatrixClient -Q_DECLARE_METATYPE(QMatrixClient::Event*) -Q_DECLARE_METATYPE(const QMatrixClient::Event*) +// 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) +{ + using event_type = fn_arg_t<FnT>; + if (is<std::decay_t<event_type>>(event)) + visitor(static_cast<event_type>(event)); +} + +// 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 = {}) +{ + 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); +} + +// 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) +{ + 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)...); +} + +// A facility overload that calls void-returning visit() on each event +// over a range of event pointers +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>> +{ + for (auto&& evtPtr: events) + visit(*evtPtr, std::forward<FnTs>(visitors)...); +} +} // namespace Quotient +Q_DECLARE_METATYPE(Quotient::Event*) +Q_DECLARE_METATYPE(const Quotient::Event*) diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp index 77f756cd..802d8176 100644 --- a/lib/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "eventcontent.h" @@ -23,7 +23,7 @@ #include <QtCore/QMimeDatabase> -using namespace QMatrixClient::EventContent; +using namespace Quotient::EventContent; QJsonObject Base::toJson() const { @@ -34,14 +34,17 @@ QJsonObject Base::toJson() const FileInfo::FileInfo(const QUrl& u, qint64 payloadSize, const QMimeType& mimeType, const QString& originalFilename) - : mimeType(mimeType), url(u), payloadSize(payloadSize) + : mimeType(mimeType) + , url(u) + , payloadSize(payloadSize) , originalName(originalFilename) -{ } +{} FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) : originalInfoJson(infoJson) - , mimeType(QMimeDatabase().mimeTypeForName(infoJson["mimetype"_ls].toString())) + , mimeType( + QMimeDatabase().mimeTypeForName(infoJson["mimetype"_ls].toString())) , url(u) , payloadSize(fromJson<qint64>(infoJson["size"_ls])) , originalName(originalFilename) @@ -53,7 +56,7 @@ FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, bool FileInfo::isValid() const { return url.scheme() == "mxc" - && (url.authority() + url.path()).count('/') == 1; + && (url.authority() + url.path()).count('/') == 1; } void FileInfo::fillInfoJson(QJsonObject* infoJson) const @@ -68,13 +71,13 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const ImageInfo::ImageInfo(const QUrl& u, qint64 fileSize, QMimeType mimeType, const QSize& imageSize, const QString& originalFilename) : FileInfo(u, fileSize, mimeType, originalFilename), imageSize(imageSize) -{ } +{} ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) : FileInfo(u, infoJson, originalFilename) , imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt()) -{ } +{} void ImageInfo::fillInfoJson(QJsonObject* infoJson) const { @@ -88,7 +91,7 @@ void ImageInfo::fillInfoJson(QJsonObject* infoJson) const Thumbnail::Thumbnail(const QJsonObject& infoJson) : ImageInfo(infoJson["thumbnail_url"_ls].toString(), infoJson["thumbnail_info"_ls].toObject()) -{ } +{} void Thumbnail::fillInfoJson(QJsonObject* infoJson) const { diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index ab31a75d..0d4c047e 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -23,262 +23,260 @@ #include <QtCore/QJsonObject> #include <QtCore/QMimeType> -#include <QtCore/QUrl> #include <QtCore/QSize> +#include <QtCore/QUrl> +#include <QtCore/QMetaType> -namespace QMatrixClient -{ - 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; +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; - // FIXME: make toJson() from converters.* work on base classes - QJsonObject toJson() const; + protected: + Base(const Base&) = default; + Base(Base&&) = default; - public: - QJsonObject originalJson; + virtual void fillJson(QJsonObject* o) const = 0; + }; - protected: - 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 = {}); - // 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. + bool isValid() const; - // 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> + void fillInfoJson(QJsonObject* infoJson) const; /** - * 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. + * \brief Extract media id from the URL * - * 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. + * This can be used, e.g., to construct a QML-facing image:// + * URI as follows: + * \code "image://provider/" + info.mediaId() \endcode */ - 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) - { - QJsonObject infoJson; - info.fillInfoJson(&infoJson); - return infoJson; - } + QString mediaId() const { return url.authority() + url.path(); } - /** - * 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 = {}); + public: + QJsonObject originalInfoJson; + QMimeType mimeType; + QUrl url; + qint64 payloadSize; + QString originalName; + }; + + template <typename InfoT> + QJsonObject toInfoJson(const InfoT& info) + { + QJsonObject infoJson; + info.fillInfoJson(&infoJson); + return infoJson; + } + + /** + * 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; - void fillInfoJson(QJsonObject* infoJson) const; + public: + QSize imageSize; + }; - 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; /** - * 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. + * Writes thumbnail information to "thumbnail_info" subobject + * and thumbnail URL to "thumbnail_url" node inside "info". */ - class Thumbnail : public ImageInfo + 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()) { - 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 + // 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 { - public: - explicit TypedBase(const QJsonObject& o = {}) : Base(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; } - }; + Q_ASSERT(json); + json->insert("url", InfoT::url.toString()); + if (!InfoT::originalName.isEmpty()) + json->insert("filename", InfoT::originalName); + json->insert("info", toInfoJson<InfoT>(*this)); + } + }; - /** - * 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 + 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) { - 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> + // 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 { - public: - 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); - } - }; + 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.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 QMatrixClient + /** + * 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 +Q_DECLARE_METATYPE(const Quotient::EventContent::TypedBase*) diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h index da663392..ebb96441 100644 --- a/lib/events/eventloader.h +++ b/lib/events/eventloader.h @@ -1,71 +1,86 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #pragma once #include "stateevent.h" -namespace QMatrixClient { - 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); - } - } - - /** 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. - */ +namespace Quotient { +namespace _impl { template <typename BaseEventT> - inline event_ptr_tt<BaseEventT> loadEvent(const QJsonObject& fullJson) + static inline auto loadEvent(const QJsonObject& json, + const QString& matrixType) { - return _impl::loadEvent<BaseEventT>(fullJson, - fullJson[TypeKeyL].toString()); + if (auto e = EventFactory<BaseEventT>::make(json, matrixType)) + return e; + return makeEvent<BaseEventT>(unknownEventTypeId(), json); } +} // namespace _impl - /** 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) +/*! 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); +} + +/*! 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 _impl::loadEvent<BaseEventT>(basicEventJson(matrixType, content), - matrixType); + return loadEvent<EventT>(jv.toObject()); } - - template <typename EventT> struct JsonConverter<event_ptr_tt<EventT>> + static auto load(const QJsonDocument& jd) { - static auto load(const QJsonValue& jv) - { - return loadEvent<EventT>(jv.toObject()); - } - static auto load(const QJsonDocument& jd) - { - return loadEvent<EventT>(jd.object()); - } - }; -} // namespace QMatrixClient + return loadEvent<EventT>(jd.object()); + } +}; +} // namespace Quotient diff --git a/lib/events/reactionevent.cpp b/lib/events/reactionevent.cpp index 0081edc2..003c8ead 100644 --- a/lib/events/reactionevent.cpp +++ b/lib/events/reactionevent.cpp @@ -13,15 +13,15 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "reactionevent.h" -using namespace QMatrixClient; +using namespace Quotient; -void QMatrixClient::JsonObjectConverter<EventRelation>::dumpTo( - QJsonObject& jo, const EventRelation& pod) +void JsonObjectConverter<EventRelation>::dumpTo( + QJsonObject& jo, const EventRelation& pod) { if (pod.type.isEmpty()) { qCWarning(MAIN) << "Empty relation type; won't dump to JSON"; @@ -33,8 +33,8 @@ void QMatrixClient::JsonObjectConverter<EventRelation>::dumpTo( jo.insert(QStringLiteral("key"), pod.key); } -void QMatrixClient::JsonObjectConverter<EventRelation>::fillFrom( - const QJsonObject& jo, EventRelation& pod) +void JsonObjectConverter<EventRelation>::fillFrom( + const QJsonObject& jo, EventRelation& pod) { // The experimental logic for generic relationships (MSC1849) fromJson(jo["rel_type"_ls], pod.type); diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h index 7a4c9b5a..75c6528c 100644 --- a/lib/events/reactionevent.h +++ b/lib/events/reactionevent.h @@ -20,13 +20,9 @@ #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { struct EventRelation { - // To please MSVC 2015 that doesn't handle initialiser lists like proper - EventRelation(QString type = {}, QString eventId = {}, QString key = {}) - : type(std::move(type)), eventId(std::move(eventId)), key(std::move(key)) - {} using reltypeid_t = const char*; static constexpr reltypeid_t Reply() { return "m.in_reply_to"; } static constexpr reltypeid_t Annotation() { return "m.annotation"; } @@ -38,26 +34,24 @@ struct EventRelation { static EventRelation replyTo(QString eventId) { - return EventRelation(Reply(), std::move(eventId)); + return { Reply(), std::move(eventId) }; } static EventRelation annotate(QString eventId, QString key) { - return EventRelation(Annotation(), std::move(eventId), std::move(key)); + return { Annotation(), std::move(eventId), std::move(key) }; } static EventRelation replace(QString eventId) { - return EventRelation(Replacement(), std::move(eventId)); + return { Replacement(), std::move(eventId) }; } }; template <> -struct JsonObjectConverter<EventRelation> -{ +struct JsonObjectConverter<EventRelation> { static void dumpTo(QJsonObject& jo, const EventRelation& pod); static void fillFrom(const QJsonObject& jo, EventRelation& pod); }; -class ReactionEvent : public RoomEvent -{ +class ReactionEvent : public RoomEvent { public: DEFINE_EVENT_TYPEID("m.reaction", ReactionEvent) @@ -65,17 +59,15 @@ public: : RoomEvent(typeId(), matrixTypeId(), { { QStringLiteral("m.relates_to"), toJson(value) } }) {} - explicit ReactionEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj) - {} + explicit ReactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj) {} EventRelation relation() const { return content<EventRelation>(QStringLiteral("m.relates_to")); } -//private: -// EventRelation _relation; +private: + EventRelation _relation; }; REGISTER_EVENT_TYPE(ReactionEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp index 47e1398c..bf050cb2 100644 --- a/lib/events/receiptevent.cpp +++ b/lib/events/receiptevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -38,32 +38,28 @@ Example of a Receipt Event: #include "converters.h" #include "logging.h" -using namespace QMatrixClient; +using namespace Quotient; -ReceiptEvent::ReceiptEvent(const QJsonObject& obj) - : Event(typeId(), obj) +ReceiptEvent::ReceiptEvent(const QJsonObject& obj) : Event(typeId(), obj) { const auto& contents = contentJson(); _eventsWithReceipts.reserve(contents.size()); - for( auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt ) - { - if (eventIt.key().isEmpty()) - { - qCWarning(EPHEMERAL) << "ReceiptEvent has an empty event id, skipping"; + for (auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt) { + if (eventIt.key().isEmpty()) { + qCWarning(EPHEMERAL) + << "ReceiptEvent has an empty event id, skipping"; qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << contents; continue; } - const QJsonObject reads = eventIt.value().toObject() - .value("m.read"_ls).toObject(); + const QJsonObject reads = + eventIt.value().toObject().value("m.read"_ls).toObject(); QVector<Receipt> receipts; receipts.reserve(reads.size()); - for( auto userIt = reads.begin(); userIt != reads.end(); ++userIt ) - { + for (auto userIt = reads.begin(); userIt != reads.end(); ++userIt) { const QJsonObject user = userIt.value().toObject(); - receipts.push_back({userIt.key(), - fromJson<QDateTime>(user["ts"_ls])}); + receipts.push_back( + { userIt.key(), fromJson<QDateTime>(user["ts"_ls]) }); } - _eventsWithReceipts.push_back({eventIt.key(), std::move(receipts)}); + _eventsWithReceipts.push_back({ eventIt.key(), std::move(receipts) }); } } - diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index c15a01c2..dd54a476 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -13,42 +13,39 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "event.h" -#include <QtCore/QVector> #include <QtCore/QDateTime> +#include <QtCore/QVector> -namespace QMatrixClient -{ - struct Receipt - { - QString userId; - QDateTime timestamp; - }; - struct ReceiptsForEvent - { - QString evtId; - QVector<Receipt> receipts; - }; - using EventsWithReceipts = QVector<ReceiptsForEvent>; +namespace Quotient { +struct Receipt { + QString userId; + QDateTime timestamp; +}; +struct ReceiptsForEvent { + QString evtId; + QVector<Receipt> receipts; +}; +using EventsWithReceipts = QVector<ReceiptsForEvent>; - class ReceiptEvent: public Event - { - public: - DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) - explicit ReceiptEvent(const QJsonObject& obj); +class ReceiptEvent : public Event { +public: + DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) + explicit ReceiptEvent(const QJsonObject& obj); - const EventsWithReceipts& eventsWithReceipts() const - { return _eventsWithReceipts; } + const EventsWithReceipts& eventsWithReceipts() const + { + return _eventsWithReceipts; + } - private: - EventsWithReceipts _eventsWithReceipts; - }; - REGISTER_EVENT_TYPE(ReceiptEvent) - DEFINE_EVENTTYPE_ALIAS(Receipt, ReceiptEvent) -} // namespace QMatrixClient +private: + EventsWithReceipts _eventsWithReceipts; +}; +REGISTER_EVENT_TYPE(ReceiptEvent) +} // namespace Quotient diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h index a72a8ff9..3b3af18e 100644 --- a/lib/events/redactionevent.h +++ b/lib/events/redactionevent.h @@ -13,29 +13,26 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class RedactionEvent : public RoomEvent - { - public: - DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) +namespace Quotient { +class RedactionEvent : public RoomEvent { +public: + DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) - explicit RedactionEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj) - { } + explicit RedactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj) + {} - QString redactedEvent() const - { return fullJson()["redacts"_ls].toString(); } - QString reason() const - { return contentJson()["reason"_ls].toString(); } - }; - REGISTER_EVENT_TYPE(RedactionEvent) - DEFINE_EVENTTYPE_ALIAS(Redaction, RedactionEvent) -} // namespace QMatrixClient + QString redactedEvent() const + { + return fullJson()["redacts"_ls].toString(); + } + QString reason() const { return contentJson()["reason"_ls].toString(); } +}; +REGISTER_EVENT_TYPE(RedactionEvent) +} // namespace Quotient diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index a43d3a85..c2100eaa 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -13,29 +13,37 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include "stateevent.h" #include "eventcontent.h" +#include "stateevent.h" + +namespace Quotient { +class RoomAvatarEvent : public StateEvent<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. +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 }) + {} -namespace QMatrixClient -{ - class RoomAvatarEvent: public StateEvent<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. - public: - DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent) - explicit RoomAvatarEvent(const QJsonObject& obj) - : StateEvent(typeId(), obj) - { } - QUrl url() const { return content().url; } - }; - REGISTER_EVENT_TYPE(RoomAvatarEvent) - DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent) -} // namespace QMatrixClient + QUrl url() const { return content().url; } +}; +REGISTER_EVENT_TYPE(RoomAvatarEvent) +} // namespace Quotient diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h new file mode 100644 index 00000000..fadfece0 --- /dev/null +++ b/lib/events/roomcanonicalaliasevent.h @@ -0,0 +1,78 @@ +/****************************************************************************** + * 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 + */ + +#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; + } + + QString canonicalAlias; + QStringList altAliases; + }; +} // namespace EventContent + +class RoomCanonicalAliasEvent + : public StateEvent<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)) + { } + + 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 8fd0f1de..c72b5bc2 100644 --- a/lib/events/roomcreateevent.cpp +++ b/lib/events/roomcreateevent.cpp @@ -1,24 +1,24 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #include "roomcreateevent.h" -using namespace QMatrixClient; +using namespace Quotient; bool RoomCreateEvent::isFederated() const { @@ -33,10 +33,8 @@ QString RoomCreateEvent::version() const 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]) - }; + return { fromJson<QString>(predJson["room_id"_ls]), + fromJson<QString>(predJson["event_id"_ls]) }; } bool RoomCreateEvent::isUpgrade() const diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h index 0a8f27cc..91aefe9e 100644 --- a/lib/events/roomcreateevent.h +++ b/lib/events/roomcreateevent.h @@ -1,49 +1,44 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #pragma once #include "stateevent.h" -namespace QMatrixClient -{ - class RoomCreateEvent : public StateEventBase - { - public: - DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) +namespace Quotient { +class RoomCreateEvent : public StateEventBase { +public: + DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) - explicit RoomCreateEvent() - : StateEventBase(typeId(), matrixTypeId()) - { } - explicit RoomCreateEvent(const QJsonObject& obj) - : StateEventBase(typeId(), obj) - { } + explicit RoomCreateEvent() : StateEventBase(typeId(), matrixTypeId()) {} + explicit RoomCreateEvent(const QJsonObject& obj) + : StateEventBase(typeId(), obj) + {} - struct Predecessor - { - QString roomId; - QString eventId; - }; - - bool isFederated() const; - QString version() const; - Predecessor predecessor() const; - bool isUpgrade() const; + struct Predecessor { + QString roomId; + QString eventId; }; - REGISTER_EVENT_TYPE(RoomCreateEvent) -} + + bool isFederated() const; + QString version() const; + Predecessor predecessor() const; + bool isUpgrade() const; +}; +REGISTER_EVENT_TYPE(RoomCreateEvent) +} // namespace Quotient diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 5e2d0b3c..a59cd6e0 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -1,59 +1,52 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #include "roomevent.h" -#include "redactionevent.h" #include "converters.h" #include "logging.h" +#include "redactionevent.h" -using namespace QMatrixClient; +using namespace Quotient; -[[gnu::unused]] static auto roomEventTypeInitialised = - Event::factory_t::chainFactory<RoomEvent>(); +[[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(Type type, const QJsonObject& json) : Event(type, json) { const auto unsignedData = json[UnsignedKeyL].toObject(); const auto redaction = unsignedData[RedactedCauseKeyL]; if (redaction.isObject()) - { _redactedBecause = makeEvent<RedactionEvent>(redaction.toObject()); - return; - } } RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job -QString RoomEvent::id() const -{ - return fullJson()[EventIdKeyL].toString(); -} +QString RoomEvent::id() const { return fullJson()[EventIdKeyL].toString(); } -QDateTime RoomEvent::timestamp() const +QDateTime RoomEvent::originTimestamp() const { - return QMatrixClient::fromJson<QDateTime>(fullJson()["origin_server_ts"_ls]); + return Quotient::fromJson<QDateTime>(fullJson()["origin_server_ts"_ls]); } QString RoomEvent::roomId() const @@ -82,7 +75,7 @@ QString RoomEvent::replacedBy() const QString RoomEvent::redactionReason() const { - return isRedacted() ? _redactedBecause->reason() : QString{}; + return isRedacted() ? _redactedBecause->reason() : QString {}; } QString RoomEvent::transactionId() const @@ -92,7 +85,17 @@ QString RoomEvent::transactionId() const QString RoomEvent::stateKey() const { - return fullJson()["state_key"_ls].toString(); + return fullJson()[StateKeyKeyL].toString(); +} + +void RoomEvent::setRoomId(const QString& roomId) +{ + editJson().insert(QStringLiteral("room_id"), roomId); +} + +void RoomEvent::setSender(const QString& senderId) +{ + editJson().insert(QStringLiteral("sender"), senderId); } void RoomEvent::setTransactionId(const QString& txnId) @@ -105,7 +108,8 @@ void RoomEvent::setTransactionId(const QString& txnId) void RoomEvent::addId(const QString& newId) { - Q_ASSERT(id().isEmpty()); Q_ASSERT(!newId.isEmpty()); + Q_ASSERT(id().isEmpty()); + Q_ASSERT(!newId.isEmpty()); editJson().insert(EventIdKey, newId); qCDebug(EVENTS) << "Event txnId -> id:" << transactionId() << "->" << id(); Q_ASSERT(id() == newId); @@ -124,7 +128,7 @@ CallEventBase::CallEventBase(Type type, event_mtype_t matrixType, const QJsonObject& contentJson) : RoomEvent(type, matrixType, makeCallContentJson(callId, version, contentJson)) -{ } +{} CallEventBase::CallEventBase(Event::Type type, const QJsonObject& json) : RoomEvent(type, json) diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index 8926ab0f..621652cb 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -1,20 +1,20 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #pragma once @@ -22,87 +22,89 @@ #include <QtCore/QDateTime> -namespace QMatrixClient -{ - class RedactionEvent; +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) - public: - using factory_t = EventFactory<RoomEvent>; +/** 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) +public: + using factory_t = EventFactory<RoomEvent>; + + // 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; - // 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; + QString id() const; + QDateTime originTimestamp() const; + [[deprecated("Use originTimestamp()")]] QDateTime timestamp() const { + return originTimestamp(); + } + QString roomId() const; + QString senderId() const; + bool isReplaced() const; + QString replacedBy() const; + bool isRedacted() const { return bool(_redactedBecause); } + const event_ptr_tt<RedactionEvent>& redactedBecause() const + { + return _redactedBecause; + } + QString redactionReason() const; + QString transactionId() const; + QString stateKey() const; - QString id() const; - QDateTime timestamp() const; - QString roomId() const; - QString senderId() const; - bool isReplaced() const; - QString replacedBy() const; - bool isRedacted() const { return bool(_redactedBecause); } - const event_ptr_tt<RedactionEvent>& redactedBecause() const - { - return _redactedBecause; - } - QString redactionReason() const; - QString transactionId() const; - QString stateKey() const; + void setRoomId(const QString& roomId); + 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() - */ - void setTransactionId(const QString& txnId); + /** + * 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() + */ + 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 - */ - void addId(const QString& newId); + /** + * 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 + */ + void addId(const QString& newId); - private: - event_ptr_tt<RedactionEvent> _redactedBecause; - }; - using RoomEventPtr = event_ptr_tt<RoomEvent>; - using RoomEvents = EventsArray<RoomEvent>; - using RoomEventsRange = Range<RoomEvents>; +private: + event_ptr_tt<RedactionEvent> _redactedBecause; +}; +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; } +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 QMatrixClient -Q_DECLARE_METATYPE(QMatrixClient::RoomEvent*) -Q_DECLARE_METATYPE(const QMatrixClient::RoomEvent*) + 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 new file mode 100644 index 00000000..66580430 --- /dev/null +++ b/lib/events/roomkeyevent.cpp @@ -0,0 +1,9 @@ +#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 new file mode 100644 index 00000000..679cbf7c --- /dev/null +++ b/lib/events/roomkeyevent.h @@ -0,0 +1,19 @@ +#pragma once + +#include "event.h" + +namespace Quotient { +class RoomKeyEvent : public Event +{ +public: + DEFINE_EVENT_TYPEID("m.room_key", RoomKeyEvent) + + RoomKeyEvent(const QJsonObject& obj); + + 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); } +}; +REGISTER_EVENT_TYPE(RoomKeyEvent) +} // namespace Quotient diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index 6da76526..3193a54d 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "roommemberevent.h" @@ -23,68 +23,91 @@ #include <array> -static const std::array<QString, 5> membershipStrings = { { - QStringLiteral("invite"), QStringLiteral("join"), - QStringLiteral("knock"), QStringLiteral("leave"), - QStringLiteral("ban") -} }; +static const std::array<QString, 5> membershipStrings = { + { QStringLiteral("invite"), QStringLiteral("join"), QStringLiteral("knock"), + QStringLiteral("leave"), QStringLiteral("ban") } +}; -namespace QMatrixClient { - template <> - struct JsonConverter<MembershipType> +namespace Quotient { +template <> +struct JsonConverter<MembershipType> { + static MembershipType load(const QJsonValue& jv) { - static MembershipType 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()); + const auto& membershipString = jv.toString(); + for (auto it = membershipStrings.begin(); it != membershipStrings.end(); + ++it) + if (membershipString == *it) + return MembershipType(it - membershipStrings.begin()); + if (!membershipString.isEmpty()) qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString; - return MembershipType::Undefined; - } - }; -} + return MembershipType::Undefined; + } +}; +} // namespace Quotient -using namespace QMatrixClient; +using namespace Quotient; MemberEventContent::MemberEventContent(const QJsonObject& json) : membership(fromJson<MembershipType>(json["membership"_ls])) , isDirect(json["is_direct"_ls].toBool()) , displayName(sanitized(json["displayname"_ls].toString())) , avatarUrl(json["avatar_url"_ls].toString()) -{ } + , reason(json["reason"_ls].toString()) +{} void MemberEventContent::fillJson(QJsonObject* o) const { Q_ASSERT(o); Q_ASSERT_X(membership != MembershipType::Undefined, __FUNCTION__, - "The key 'membership' must be explicit in MemberEventContent"); + "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()); + if (!reason.isEmpty()) + o->insert(QStringLiteral("reason"), reason); +} + +bool RoomMemberEvent::changesMembership() const +{ + return !prevContent() || prevContent()->membership != membership(); } bool RoomMemberEvent::isInvite() const { - return membership() == MembershipType::Invite && - (!prevContent() || prevContent()->membership != membership()); + return membership() == MembershipType::Invite && changesMembership(); +} + +bool RoomMemberEvent::isRejectedInvite() const +{ + return membership() == MembershipType::Leave && prevContent() + && prevContent()->membership == MembershipType::Invite; } bool RoomMemberEvent::isJoin() const { - return membership() == MembershipType::Join && - (!prevContent() || prevContent()->membership != membership()); + return membership() == MembershipType::Join && changesMembership(); } bool RoomMemberEvent::isLeave() const { - return membership() == MembershipType::Leave && - prevContent() && prevContent()->membership != membership() && - prevContent()->membership != MembershipType::Ban; + return membership() == MembershipType::Leave && prevContent() + && prevContent()->membership != membership() + && prevContent()->membership != MembershipType::Ban + && prevContent()->membership != MembershipType::Invite; +} + +bool RoomMemberEvent::isBan() const +{ + return membership() == MembershipType::Ban && changesMembership(); +} + +bool RoomMemberEvent::isUnban() const +{ + return membership() == MembershipType::Leave && prevContent() + && prevContent()->membership == MembershipType::Ban; } bool RoomMemberEvent::isRename() const diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index b8224033..783b8207 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -13,94 +13,103 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include "stateevent.h" #include "eventcontent.h" +#include "stateevent.h" -namespace QMatrixClient -{ - class MemberEventContent: public EventContent::Base - { - public: - enum MembershipType : size_t { Invite = 0, Join, Knock, Leave, Ban, - Undefined }; +namespace Quotient { +class MemberEventContent : public EventContent::Base { +public: + enum MembershipType : size_t { + Invite = 0, + Join, + Knock, + Leave, + Ban, + Undefined + }; - explicit MemberEventContent(MembershipType mt = Join) - : membership(mt) - { } - explicit MemberEventContent(const QJsonObject& json); + explicit MemberEventContent(MembershipType mt = Join) : membership(mt) {} + explicit MemberEventContent(const QJsonObject& json); - MembershipType membership; - bool isDirect = false; - QString displayName; - QUrl avatarUrl; + MembershipType membership; + bool isDirect = false; + QString displayName; + QUrl avatarUrl; + QString reason; - protected: - void fillJson(QJsonObject* o) const override; - }; +protected: + void fillJson(QJsonObject* o) const override; +}; - using MembershipType = MemberEventContent::MembershipType; +using MembershipType = MemberEventContent::MembershipType; - class RoomMemberEvent: public StateEvent<MemberEventContent> - { - Q_GADGET - public: - DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) +class RoomMemberEvent : public StateEvent<MemberEventContent> { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) - using MembershipType = MemberEventContent::MembershipType; + using MembershipType = MemberEventContent::MembershipType; - explicit RoomMemberEvent(const QJsonObject& obj) - : StateEvent(typeId(), obj) - { } - RoomMemberEvent(MemberEventContent&& c) - : StateEvent(typeId(), matrixTypeId(), c) - { } + 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)...) + {} - /// 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) - { } + /// 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) + {} - MembershipType membership() const { return content().membership; } - QString userId() const - { return fullJson()["state_key"_ls].toString(); } - bool isDirect() const { return content().isDirect; } - QString displayName() const { return content().displayName; } - QUrl avatarUrl() const { return content().avatarUrl; } - bool isInvite() const; - bool isJoin() const; - bool isLeave() const; - bool isRename() const; - bool isAvatarUpdate() const; + MembershipType membership() const { return content().membership; } + QString userId() const { return fullJson()[StateKeyKeyL].toString(); } + bool isDirect() const { return content().isDirect; } + QString displayName() const { return content().displayName; } + QUrl avatarUrl() const { return content().avatarUrl; } + QString reason() const { return content().reason; } + bool changesMembership() const; + bool isBan() const; + bool isUnban() const; + bool isInvite() const; + bool isRejectedInvite() const; + bool isJoin() const; + bool isLeave() const; + bool isRename() const; + bool isAvatarUpdate() const; - private: - REGISTER_ENUM(MembershipType) - }; +private: + Q_ENUM(MembershipType) +}; - template <> - class EventFactory<RoomMemberEvent> +template <> +class EventFactory<RoomMemberEvent> { +public: + static event_ptr_tt<RoomMemberEvent> make(const QJsonObject& json, + const QString&) { - public: - static event_ptr_tt<RoomMemberEvent> make(const QJsonObject& json, - const QString&) - { - return makeEvent<RoomMemberEvent>(json); - } - }; + return makeEvent<RoomMemberEvent>(json); + } +}; - REGISTER_EVENT_TYPE(RoomMemberEvent) - DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) -} // namespace QMatrixClient +REGISTER_EVENT_TYPE(RoomMemberEvent) +} // namespace Quotient diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 1edf82e4..616a034f 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -13,26 +13,25 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "roommessageevent.h" #include "logging.h" -#include <QtCore/QMimeDatabase> #include <QtCore/QFileInfo> +#include <QtCore/QMimeDatabase> #include <QtGui/QImageReader> #include <QtMultimedia/QMediaResource> -using namespace QMatrixClient; +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 BodyKeyL = "body"_ls; static const auto FormattedBodyKeyL = "formatted_body"_ls; static const auto TextTypeKey = "m.text"; @@ -51,31 +50,33 @@ template <> TypedBase* make<TextContent>(const QJsonObject& json) { return json.contains(FormattedBodyKeyL) || json.contains(RelatesToKeyL) - ? new TextContent(json) : nullptr; + ? new TextContent(json) + : nullptr; } -struct MsgTypeDesc -{ +struct MsgTypeDesc { QString matrixType; MsgType enumType; TypedBase* (*maker)(const QJsonObject&); }; -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> } - }; +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> } +}; QString msgTypeToJson(MsgType enumType) { auto it = std::find_if(msgTypes.begin(), msgTypes.end(), - [=](const MsgTypeDesc& mtd) { return mtd.enumType == enumType; }); + [=](const MsgTypeDesc& mtd) { + return mtd.enumType == enumType; + }); if (it != msgTypes.end()) return it->matrixType; @@ -85,15 +86,23 @@ QString msgTypeToJson(MsgType enumType) MsgType jsonToMsgType(const QString& matrixType) { auto it = std::find_if(msgTypes.begin(), msgTypes.end(), - [=](const MsgTypeDesc& mtd) { return mtd.matrixType == matrixType; }); + [=](const MsgTypeDesc& mtd) { + return mtd.matrixType == matrixType; + }); if (it != msgTypes.end()) return it->enumType; return MsgType::Unknown; } +inline bool isReplacement(const Omittable<RelatesTo>& rel) +{ + return rel && rel->type == RelatesTo::ReplacementTypeId(); +} + QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, - const QString& jsonMsgType, TypedBase* content) + const QString& jsonMsgType, + TypedBase* content) { auto json = content ? content->toJson() : QJsonObject(); if (json.contains(RelatesToKeyL)) { @@ -107,9 +116,10 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, // 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(BodyKeyL, plainBody); + newContentJson.insert(BodyKey, plainBody); newContentJson.insert(MsgTypeKeyL, jsonMsgType); json.insert(QStringLiteral("m.new_content"), newContentJson); json[MsgTypeKeyL] = jsonMsgType; @@ -124,24 +134,24 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, } RoomMessageEvent::RoomMessageEvent(const QString& plainBody, - const QString& jsonMsgType, TypedBase* content) + const QString& jsonMsgType, + TypedBase* content) : RoomEvent(typeId(), matrixTypeId(), assembleContentJson(plainBody, jsonMsgType, content)) , _content(content) -{ } +{} -RoomMessageEvent::RoomMessageEvent(const QString& plainBody, - MsgType msgType, TypedBase* content) +RoomMessageEvent::RoomMessageEvent(const QString& plainBody, MsgType msgType, + TypedBase* content) : RoomMessageEvent(plainBody, msgTypeToJson(msgType), content) -{ } +{} TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile) { auto filePath = file.absoluteFilePath(); auto localUrl = QUrl::fromLocalFile(filePath); auto mimeType = QMimeDatabase().mimeTypeForFile(file); - if (!asGenericFile) - { + if (!asGenericFile) { auto mimeTypeName = mimeType.name(); if (mimeTypeName.startsWith("image/")) return new ImageContent(localUrl, file.size(), mimeType, @@ -163,11 +173,12 @@ TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile) } RoomMessageEvent::RoomMessageEvent(const QString& plainBody, - const QFileInfo& file, bool asGenericFile) + const QFileInfo& file, bool asGenericFile) : RoomMessageEvent(plainBody, - asGenericFile ? QStringLiteral("m.file") : rawMsgTypeForFile(file), - contentFromFile(file, asGenericFile)) -{ } + asGenericFile ? QStringLiteral("m.file") + : rawMsgTypeForFile(file), + contentFromFile(file, asGenericFile)) +{} RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj), _content(nullptr) @@ -175,26 +186,21 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) if (isRedacted()) return; const QJsonObject content = contentJson(); - if ( content.contains(MsgTypeKeyL) && content.contains(BodyKeyL) ) - { + if (content.contains(MsgTypeKeyL) && content.contains(BodyKeyL)) { auto msgtype = content[MsgTypeKeyL].toString(); bool msgTypeFound = false; - for (const auto& mt: msgTypes) - if (mt.matrixType == msgtype) - { + for (const auto& mt : msgTypes) + if (mt.matrixType == msgtype) { _content.reset(mt.maker(content)); msgTypeFound = true; } - if (!msgTypeFound) - { + if (!msgTypeFound) { qCWarning(EVENTS) << "RoomMessageEvent: unknown msg_type," << " full content dump follows"; qCWarning(EVENTS) << formatJson << content; } - } - else - { + } else { qCWarning(EVENTS) << "No body or msgtype in room message event"; qCWarning(EVENTS) << formatJson << obj; } @@ -218,15 +224,15 @@ QString RoomMessageEvent::plainBody() const QMimeType RoomMessageEvent::mimeType() const { static const auto PlainTextMimeType = - QMimeDatabase().mimeTypeForName("text/plain"); + QMimeDatabase().mimeTypeForName("text/plain"); return _content ? _content->type() : PlainTextMimeType; } bool RoomMessageEvent::hasTextContent() const { - return !content() || - (msgtype() == MsgType::Text || msgtype() == MsgType::Emote || - msgtype() == MsgType::Notice); + return !content() + || (msgtype() == MsgType::Text || msgtype() == MsgType::Emote + || msgtype() == MsgType::Notice); } bool RoomMessageEvent::hasFileContent() const @@ -245,17 +251,18 @@ QString RoomMessageEvent::replacedEvent() const return {}; const auto& rel = static_cast<const TextContent*>(content())->relatesTo; - return !rel.omitted() && rel->type == RelatesTo::ReplacementTypeId() - ? rel->eventId : QString(); + return isReplacement(rel) ? rel->eventId : QString(); } QString rawMsgTypeForMimeType(const QMimeType& mimeType) { auto name = mimeType.name(); - return name.startsWith("image/") ? QStringLiteral("m.image") : - name.startsWith("video/") ? QStringLiteral("m.video") : - name.startsWith("audio/") ? QStringLiteral("m.audio") : - QStringLiteral("m.file"); + return name.startsWith("image/") + ? QStringLiteral("m.image") + : name.startsWith("video/") + ? QStringLiteral("m.video") + : name.startsWith("audio/") ? QStringLiteral("m.audio") + : QStringLiteral("m.file"); } QString RoomMessageEvent::rawMsgTypeForUrl(const QUrl& url) @@ -268,18 +275,21 @@ QString RoomMessageEvent::rawMsgTypeForFile(const QFileInfo& fi) return rawMsgTypeForMimeType(QMimeDatabase().mimeTypeForFile(fi)); } -TextContent::TextContent(const QString& text, const QString& contentType, +TextContent::TextContent(QString text, const QString& contentType, Omittable<RelatesTo> relatesTo) - : mimeType(QMimeDatabase().mimeTypeForName(contentType)), body(text) + : mimeType(QMimeDatabase().mimeTypeForName(contentType)) + , body(std::move(text)) , relatesTo(std::move(relatesTo)) { if (contentType == HtmlContentTypeId) mimeType = QMimeDatabase().mimeTypeForName("text/html"); } -namespace QMatrixClient -{ -Omittable<RelatesTo> relationFromJson(const QJsonValue& jv) +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()) @@ -291,22 +301,21 @@ Omittable<RelatesTo> relationFromJson(const QJsonValue& jv) return RelatesTo { jo.value("rel_type"_ls).toString(), jo.value(EventIdKeyL).toString() }; } -} +} // namespace Quotient TextContent::TextContent(const QJsonObject& json) - : relatesTo(relationFromJson(json[RelatesToKeyL])) + : relatesTo(fromJson<Omittable<RelatesTo>>(json[RelatesToKeyL])) { QMimeDatabase db; static const auto PlainTextMimeType = db.mimeTypeForName("text/plain"); static const auto HtmlMimeType = db.mimeTypeForName("text/html"); - const auto actualJson = - relatesTo.omitted() || relatesTo->type != RelatesTo::ReplacementTypeId() - ? json : json.value("m.new_content"_ls).toObject(); + const auto actualJson = isReplacement(relatesTo) + ? json.value("m.new_content"_ls).toObject() + : json; // Special-casing the custom matrix.org's (actually, Riot's) way // of sending HTML messages. - if (actualJson["format"_ls].toString() == HtmlContentTypeId) - { + if (actualJson["format"_ls].toString() == HtmlContentTypeId) { mimeType = HtmlMimeType; body = actualJson[FormattedBodyKeyL].toString(); } else { @@ -320,22 +329,21 @@ TextContent::TextContent(const QJsonObject& json) void TextContent::fillJson(QJsonObject* json) const { static const auto FormatKey = QStringLiteral("format"); - static const auto RichBodyKey = QStringLiteral("formatted_body"); + static const auto FormattedBodyKey = QStringLiteral("formatted_body"); Q_ASSERT(json); - if (mimeType.inherits("text/html")) - { + if (mimeType.inherits("text/html")) { json->insert(FormatKey, HtmlContentTypeId); - json->insert(RichBodyKey, body); + json->insert(FormattedBodyKey, body); } - if (!relatesTo.omitted()) { + if (relatesTo) { json->insert(QStringLiteral("m.relates_to"), - QJsonObject { { relatesTo->type, relatesTo->eventId } }); + QJsonObject { { "rel_type", relatesTo->type }, { EventIdKey, relatesTo->eventId } }); if (relatesTo->type == RelatesTo::ReplacementTypeId()) { QJsonObject newContentJson; if (mimeType.inherits("text/html")) { json->insert(FormatKey, HtmlContentTypeId); - json->insert(RichBodyKey, body); + json->insert(FormattedBodyKey, body); } json->insert(QStringLiteral("m.new_content"), newContentJson); } @@ -345,13 +353,13 @@ void TextContent::fillJson(QJsonObject* json) const LocationContent::LocationContent(const QString& geoUri, const Thumbnail& thumbnail) : geoUri(geoUri), thumbnail(thumbnail) -{ } +{} LocationContent::LocationContent(const QJsonObject& json) : TypedBase(json) , geoUri(json["geo_uri"_ls].toString()) , thumbnail(json["info"_ls].toObject()) -{ } +{} QMimeType LocationContent::type() const { diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index 7320e4ea..2501d097 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -13,211 +13,211 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include "roomevent.h" #include "eventcontent.h" +#include "roomevent.h" class QFileInfo; -namespace QMatrixClient -{ - namespace MessageEventContent = EventContent; // Back-compatibility +namespace Quotient { +namespace MessageEventContent = EventContent; // Back-compatibility + +/** + * The event class corresponding to m.room.message events + */ +class 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) + + enum class MsgType { + Text, + Emote, + Notice, + Image, + File, + Location, + Video, + Audio, + Unknown + }; + + RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, + EventContent::TypedBase* content = nullptr); + explicit RoomMessageEvent(const QString& plainBody, + MsgType msgType = MsgType::Text, + EventContent::TypedBase* content = nullptr); + explicit RoomMessageEvent(const QString& plainBody, const QFileInfo& file, + bool asGenericFile = false); + explicit RoomMessageEvent(const QJsonObject& obj); + + MsgType msgtype() const; + QString rawMsgtype() const; + QString plainBody() const; + const EventContent::TypedBase* content() const { return _content.data(); } + template <typename VisitorT> + void editContent(VisitorT&& visitor) + { + visitor(*_content); + editJson()[ContentKeyL] = assembleContentJson(plainBody(), rawMsgtype(), + _content.data()); + } + QMimeType mimeType() const; + bool hasTextContent() const; + bool hasFileContent() const; + bool hasThumbnail() const; + QString replacedEvent() const; + + static QString rawMsgTypeForUrl(const QUrl& url); + static QString rawMsgTypeForFile(const QFileInfo& fi); + +private: + QScopedPointer<EventContent::TypedBase> _content; + + // FIXME: should it really be static? + static QJsonObject assembleContentJson(const QString& plainBody, + const QString& jsonMsgType, + EventContent::TypedBase* content); + + 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; + }; + inline RelatesTo replyTo(QString eventId) + { + return { RelatesTo::ReplyTypeId(), std::move(eventId) }; + } + inline RelatesTo replacementOf(QString eventId) + { + return { RelatesTo::ReplacementTypeId(), std::move(eventId) }; + } /** - * The event class corresponding to m.room.message events + * 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 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(EventContent::TypedBase* content READ content CONSTANT) - public: - DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent) - - enum class MsgType - { - Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown - }; - - RoomMessageEvent(const QString& plainBody, - const QString& jsonMsgType, - EventContent::TypedBase* content = nullptr); - explicit RoomMessageEvent(const QString& plainBody, - MsgType msgType = MsgType::Text, - EventContent::TypedBase* content = nullptr); - explicit RoomMessageEvent(const QString& plainBody, - const QFileInfo& file, - bool asGenericFile = false); - explicit RoomMessageEvent(const QJsonObject& obj); - - MsgType msgtype() const; - QString rawMsgtype() const; - QString plainBody() const; - EventContent::TypedBase* content() const - { return _content.data(); } - template <typename VisitorT> - void editContent(VisitorT visitor) - { - visitor(*_content); - editJson()[ContentKeyL] = - assembleContentJson(plainBody(), rawMsgtype(), content()); - } - QMimeType mimeType() const; - bool hasTextContent() const; - bool hasFileContent() const; - bool hasThumbnail() const; - QString replacedEvent() const; - - static QString rawMsgTypeForUrl(const QUrl& url); - static QString rawMsgTypeForFile(const QFileInfo& fi); - - private: - QScopedPointer<EventContent::TypedBase> _content; - - // FIXME: should it really be static? - static QJsonObject assembleContentJson(const QString& plainBody, - const QString& jsonMsgType, EventContent::TypedBase* content); - - REGISTER_ENUM(MsgType) + class TextContent : public TypedBase { + public: + TextContent(QString text, const QString& contentType, + Omittable<RelatesTo> relatesTo = none); + explicit TextContent(const QJsonObject& json); + + QMimeType type() const override { return mimeType; } + + QMimeType mimeType; + QString body; + Omittable<RelatesTo> relatesTo; + + protected: + void fillJson(QJsonObject* json) const override; }; - REGISTER_EVENT_TYPE(RoomMessageEvent) - DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent) - using MessageEventType = RoomMessageEvent::MsgType; - namespace EventContent - { - // Additional event content types + /** + * Content class for m.location + * + * Available fields: + * - corresponding to the top-level JSON: + * - geoUri ("geo_uri" in JSON) + * - corresponding to the "info" subobject: + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: + * - thumbnail.payloadSize + * - thumbnail.mimeType + * - thumbnail.imageSize + */ + class LocationContent : public TypedBase { + public: + LocationContent(const QString& geoUri, const Thumbnail& thumbnail = {}); + explicit LocationContent(const QJsonObject& json); - 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; - }; - inline RelatesTo replyTo(QString eventId) + QMimeType type() const override; + + public: + QString geoUri; + Thumbnail thumbnail; + + protected: + 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 { + public: + using ContentT::ContentT; + PlayableContent(const QJsonObject& json) + : ContentT(json) + , duration(ContentT::originalInfoJson["duration"_ls].toInt()) + {} + + protected: + void fillJson(QJsonObject* json) const override { - return { RelatesTo::ReplyTypeId(), std::move(eventId) }; + ContentT::fillJson(json); + auto infoJson = json->take("info"_ls).toObject(); + infoJson.insert(QStringLiteral("duration"), duration); + json->insert(QStringLiteral("info"), infoJson); } - /** - * 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 - { - public: - TextContent(const QString& text, const QString& contentType, - Omittable<RelatesTo> relatesTo = none); - explicit TextContent(const QJsonObject& json); - - QMimeType type() const override { return mimeType; } - - QMimeType mimeType; - QString body; - Omittable<RelatesTo> relatesTo; - - protected: - void fillJson(QJsonObject* json) const override; - }; - - /** - * Content class for m.location - * - * Available fields: - * - corresponding to the top-level JSON: - * - geoUri ("geo_uri" in JSON) - * - corresponding to the "info" subobject: - * - thumbnail.url ("thumbnail_url" in JSON) - * - corresponding to the "info/thumbnail_info" subobject: - * - thumbnail.payloadSize - * - thumbnail.mimeType - * - thumbnail.imageSize - */ - class LocationContent: public TypedBase - { - public: - LocationContent(const QString& geoUri, - const Thumbnail& thumbnail = {}); - explicit LocationContent(const QJsonObject& json); - - QMimeType type() const override; - - public: - QString geoUri; - Thumbnail thumbnail; - - protected: - 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 - { - public: - using ContentT::ContentT; - PlayableContent(const QJsonObject& json) - : ContentT(json) - , duration(ContentT::originalInfoJson["duration"_ls].toInt()) - { } - - protected: - void fillJson(QJsonObject* json) const override - { - ContentT::fillJson(json); - auto infoJson = json->take("info"_ls).toObject(); - infoJson.insert(QStringLiteral("duration"), duration); - json->insert(QStringLiteral("info"), infoJson); - } - - public: - int duration; - }; - - /** - * Content class for m.video - * - * Available fields: - * - corresponding to the top-level JSON: - * - url - * - filename (extension to the CS API spec) - * - corresponding to the "info" subobject: - * - payloadSize ("size" in JSON) - * - mimeType ("mimetype" in JSON) - * - duration - * - 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 "info": - * - payloadSize - * - mimeType - * - imageSize - */ - using VideoContent = PlayableContent<UrlWithThumbnailContent<ImageInfo>>; - - /** - * Content class for m.audio - * - * Available fields: - * - corresponding to the top-level JSON: - * - url - * - filename (extension to the CS API spec) - * - corresponding to the "info" subobject: - * - payloadSize ("size" in JSON) - * - mimeType ("mimetype" in JSON) - * - duration - */ - using AudioContent = PlayableContent<UrlBasedContent<FileInfo>>; - } // namespace EventContent -} // namespace QMatrixClient + public: + int duration; + }; + + /** + * Content class for m.video + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename (extension to the CS API spec) + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - duration + * - 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 "info": + * - payloadSize + * - mimeType + * - imageSize + */ + using VideoContent = PlayableContent<UrlWithThumbnailContent<ImageInfo>>; + + /** + * Content class for m.audio + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename (extension to the CS API spec) + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - duration + */ + using AudioContent = PlayableContent<UrlBasedContent<FileInfo>>; +} // namespace EventContent +} // namespace Quotient diff --git a/lib/events/roompowerlevelsevent.cpp b/lib/events/roompowerlevelsevent.cpp new file mode 100644 index 00000000..0a401752 --- /dev/null +++ b/lib/events/roompowerlevelsevent.cpp @@ -0,0 +1,62 @@ +#include "roompowerlevelsevent.h" + +#include <QJsonDocument> + +using namespace Quotient; + +PowerLevelsEventContent::PowerLevelsEventContent(const QJsonObject& json) : + invite(json["invite"_ls].toInt(50)), + kick(json["kick"_ls].toInt(50)), + ban(json["ban"_ls].toInt(50)), + redact(json["redact"_ls].toInt(50)), + events(fromJson<QHash<QString, int>>(json["events"_ls])), + eventsDefault(json["events_default"_ls].toInt(0)), + stateDefault(json["state_default"_ls].toInt(0)), + 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}}); +} + +int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) const { + auto e = events(); + + if (e.contains(eventId)) { + return e[eventId]; + } + + return eventsDefault(); +} + +int RoomPowerLevelsEvent::powerLevelForState(const QString &eventId) const { + auto e = events(); + + if (e.contains(eventId)) { + return e[eventId]; + } + + return stateDefault(); +} + +int RoomPowerLevelsEvent::powerLevelForUser(const QString &userId) const { + auto u = users(); + + if (u.contains(userId)) { + return u[userId]; + } + + return usersDefault(); +} diff --git a/lib/events/roompowerlevelsevent.h b/lib/events/roompowerlevelsevent.h new file mode 100644 index 00000000..f0f7207f --- /dev/null +++ b/lib/events/roompowerlevelsevent.h @@ -0,0 +1,76 @@ +#pragma once + +#include "eventcontent.h" +#include "stateevent.h" + +namespace Quotient { +class PowerLevelsEventContent : public EventContent::Base { +public: + struct Notifications { + int room; + }; + + explicit PowerLevelsEventContent(const QJsonObject& json); + + int invite; + int kick; + int ban; + + int redact; + + QHash<QString, int> events; + int eventsDefault; + int stateDefault; + + QHash<QString, int> users; + int usersDefault; + + Notifications notifications; + +protected: + void fillJson(QJsonObject* o) const override; +}; + +class RoomPowerLevelsEvent : public StateEvent<PowerLevelsEventContent> { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.room.power_levels", RoomPowerLevelsEvent) + + explicit RoomPowerLevelsEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj) + {} + + int invite() const { return content().invite; } + int kick() const { return content().kick; } + int ban() const { return content().ban; } + + int redact() const { return content().redact; } + + QHash<QString, int> events() const { return content().events; } + int eventsDefault() const { return content().eventsDefault; } + int stateDefault() const { return content().stateDefault; } + + QHash<QString, int> users() const { return content().users; } + int usersDefault() const { return content().usersDefault; } + + int roomNotification() const { return content().notifications.room; } + + 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 9c3bafd4..f93eb60d 100644 --- a/lib/events/roomtombstoneevent.cpp +++ b/lib/events/roomtombstoneevent.cpp @@ -1,24 +1,24 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #include "roomtombstoneevent.h" -using namespace QMatrixClient; +using namespace Quotient; QString RoomTombstoneEvent::serverMessage() const { diff --git a/lib/events/roomtombstoneevent.h b/lib/events/roomtombstoneevent.h index c7008ec4..2c2f0663 100644 --- a/lib/events/roomtombstoneevent.h +++ b/lib/events/roomtombstoneevent.h @@ -1,41 +1,37 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #pragma once #include "stateevent.h" -namespace QMatrixClient -{ - class RoomTombstoneEvent : public StateEventBase - { - public: - DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) +namespace Quotient { +class RoomTombstoneEvent : public StateEventBase { +public: + DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) - explicit RoomTombstoneEvent() - : StateEventBase(typeId(), matrixTypeId()) - { } - explicit RoomTombstoneEvent(const QJsonObject& obj) - : StateEventBase(typeId(), obj) - { } + explicit RoomTombstoneEvent() : StateEventBase(typeId(), matrixTypeId()) {} + explicit RoomTombstoneEvent(const QJsonObject& obj) + : StateEventBase(typeId(), obj) + {} - QString serverMessage() const; - QString successorRoomId() const; - }; - REGISTER_EVENT_TYPE(RoomTombstoneEvent) -} + 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 2c23d9ca..cde5b0fd 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -13,80 +13,77 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "stateevent.h" -#include "converters.h" +namespace Quotient { +namespace EventContent { + template <typename T> + class SimpleContent { + public: + using value_type = T; -namespace QMatrixClient -{ - namespace EventContent - { - template <typename T> - class SimpleContent + // 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 { - public: - using value_type = T; + return { { key, Quotient::toJson(value) } }; + } - // 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, QMatrixClient::toJson(value) } }; - } + public: + T value; - public: - T value; + protected: + QString key; + }; +} // namespace EventContent - 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(), \ - 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) \ +#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_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) - DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) - DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", - QStringList, aliases) - DEFINE_EVENTTYPE_ALIAS(RoomAliases, RoomAliasesEvent) - DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", - QString, alias) - DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent) - DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic) - DEFINE_EVENTTYPE_ALIAS(RoomTopic, RoomTopicEvent) - DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", - QString, algorithm) - DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) -} // namespace QMatrixClient +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>> { +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) + {} + QString server() const { return stateKey(); } + QStringList aliases() const { return content().value; } +}; +REGISTER_EVENT_TYPE(RoomAliasesEvent) +} // namespace Quotient diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index a84f302b..5909e8a6 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -1,33 +1,32 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #include "stateevent.h" -using namespace QMatrixClient; +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. -[[gnu::unused]] static auto stateEventTypeInitialised = +[[maybe_unused]] static auto stateEventTypeInitialised = RoomEvent::factory_t::addMethod( - [] (const QJsonObject& json, const QString& matrixType) -> StateEventPtr - { - if (!json.contains("state_key"_ls)) + [](const QJsonObject& json, const QString& matrixType) -> StateEventPtr { + if (!json.contains(StateKeyKeyL)) return nullptr; if (auto e = StateEventBase::factory_t::make(json, matrixType)) @@ -36,6 +35,12 @@ using namespace QMatrixClient; return makeEvent<StateEventBase>(unknownEventTypeId(), json); }); +StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType, + const QString& stateKey, + const QJsonObject& contentJson) + : RoomEvent(type, basicStateEventJson(matrixType, contentJson, stateKey)) +{} + bool StateEventBase::repeatsState() const { const auto prevContentJson = unsignedJson().value(PrevContentKeyL); @@ -53,6 +58,7 @@ void StateEventBase::dumpTo(QDebug dbg) const dbg << '<' << stateKey() << "> "; if (unsignedJson().contains(PrevContentKeyL)) dbg << QJsonDocument(unsignedJson()[PrevContentKeyL].toObject()) - .toJson(QJsonDocument::Compact) << " -> "; + .toJson(QJsonDocument::Compact) + << " -> "; RoomEvent::dumpTo(dbg); } diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 3f54f7bf..710b4271 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -1,121 +1,131 @@ /****************************************************************************** -* 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 -*/ + * 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 + */ #pragma once #include "roomevent.h" -namespace QMatrixClient { - class StateEventBase: public RoomEvent - { - public: - using factory_t = EventFactory<StateEventBase>; +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 { +public: + using factory_t = EventFactory<StateEventBase>; - using RoomEvent::RoomEvent; - ~StateEventBase() override = default; + 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; - bool isStateEvent() const override { return true; } - QString replacedState() const; - void dumpTo(QDebug dbg) const override; + bool isStateEvent() const override { return true; } + QString replacedState() const; + void dumpTo(QDebug dbg) const override; + + virtual bool repeatsState() const; +}; +using StateEventPtr = event_ptr_tt<StateEventBase>; +using StateEvents = EventsArray<StateEventBase>; + +template <> +inline bool is<StateEventBase>(const Event& e) +{ + return e.isStateEvent(); +} - virtual bool repeatsState() const; - }; - using StateEventPtr = event_ptr_tt<StateEventBase>; - using StateEvents = EventsArray<StateEventBase>; +/** + * A combination of event type and state key uniquely identifies a piece + * of state in Matrix. + * \sa + * https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events + */ +using StateEventKey = QPair<QString, QString>; - template <> - inline bool is<StateEventBase>(const Event& e) { return e.isStateEvent(); } +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)...) + {} - /** - * A combination of event type and state key uniquely identifies a piece - * of state in Matrix. - * \sa https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events - */ - using StateEventKey = QPair<QString, QString>; + QString senderId; + ContentT content; +}; - template <typename ContentT> - struct Prev +template <typename ContentT> +class StateEvent : public StateEventBase { +public: + using content_type = ContentT; + + template <typename... ContentParamTs> + explicit StateEvent(Type type, const QJsonObject& fullJson, + ContentParamTs&&... contentParams) + : StateEventBase(type, fullJson) + , _content(contentJson(), std::forward<ContentParamTs>(contentParams)...) { - 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; - }; - - template <typename ContentT> - class StateEvent: public StateEventBase + 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)...) { - public: - using content_type = ContentT; - - 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, - ContentParamTs&&... contentParams) - : StateEventBase(type, matrixType) - , _content(std::forward<ContentParamTs>(contentParams)...) - { - editJson().insert(ContentKey, _content.toJson()); - } - - 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; } - QString prevSenderId() const - { return _prev ? _prev->senderId : QString(); } - - private: - ContentT _content; - std::unique_ptr<Prev<ContentT>> _prev; - }; -} // namespace QMatrixClient - -namespace std { - template <> struct hash<QMatrixClient::StateEventKey> + editJson().insert(ContentKey, _content.toJson()); + } + + const ContentT& content() const { return _content; } + template <typename VisitorT> + void editContent(VisitorT&& visitor) { - size_t operator()(const QMatrixClient::StateEventKey& k) const Q_DECL_NOEXCEPT - { - return qHash(k); - } - }; -} + 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; + } + QString prevSenderId() const { return _prev ? _prev->senderId : QString(); } + +private: + ContentT _content; + std::unique_ptr<Prev<ContentT>> _prev; +}; +} // namespace Quotient diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp index 0d39d1be..a95d2f0d 100644 --- a/lib/events/typingevent.cpp +++ b/lib/events/typingevent.cpp @@ -13,20 +13,19 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "typingevent.h" #include <QtCore/QJsonArray> -using namespace QMatrixClient; +using namespace Quotient; -TypingEvent::TypingEvent(const QJsonObject& obj) - : Event(typeId(), obj) +TypingEvent::TypingEvent(const QJsonObject& obj) : Event(typeId(), obj) { const auto& array = contentJson()["user_ids"_ls].toArray(); - for(const auto& user: array ) + _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 27b668b4..1cf4e69d 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -13,27 +13,24 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "event.h" -namespace QMatrixClient -{ - class TypingEvent: public Event - { - public: - DEFINE_EVENT_TYPEID("m.typing", TypingEvent) +namespace Quotient { +class TypingEvent : public Event { +public: + DEFINE_EVENT_TYPEID("m.typing", TypingEvent) - TypingEvent(const QJsonObject& obj); + TypingEvent(const QJsonObject& obj); - const QStringList& users() const { return _users; } + const QStringList& users() const { return _users; } - private: - QStringList _users; - }; - REGISTER_EVENT_TYPE(TypingEvent) - DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent) -} // namespace QMatrixClient +private: + QStringList _users; +}; +REGISTER_EVENT_TYPE(TypingEvent) +} // namespace Quotient |