diff options
Diffstat (limited to 'events')
-rw-r--r-- | events/event.cpp | 48 | ||||
-rw-r--r-- | events/event.h | 127 | ||||
-rw-r--r-- | events/receiptevent.cpp | 6 | ||||
-rw-r--r-- | events/receiptevent.h | 4 | ||||
-rw-r--r-- | events/redactionevent.cpp | 1 | ||||
-rw-r--r-- | events/redactionevent.h | 43 | ||||
-rw-r--r-- | events/roommessageevent.cpp | 6 |
7 files changed, 194 insertions, 41 deletions
diff --git a/events/event.cpp b/events/event.cpp index 44b742c1..e74bc114 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -24,6 +24,7 @@ #include "roomavatarevent.h" #include "typingevent.h" #include "receiptevent.h" +#include "redactionevent.h" #include "logging.h" #include <QtCore/QJsonDocument> @@ -33,13 +34,16 @@ using namespace QMatrixClient; Event::Event(Type type, const QJsonObject& rep) : _type(type), _originalJson(rep) { - if (!rep.contains("content")) + if (!rep.contains("content") && + !rep.value("unsigned").toObject().contains("redacted_because")) { qCWarning(EVENTS) << "Event without 'content' node"; qCWarning(EVENTS) << formatJson << rep; } } +Event::~Event() = default; + QByteArray Event::originalJson() const { return QJsonDocument(_originalJson).toJson(); @@ -70,23 +74,25 @@ inline BaseEventT* makeIfMatches(const QJsonObject& o, const QString& selector) return makeIfMatches<BaseEventT, EventTs...>(o, selector); } -Event* Event::fromJson(const QJsonObject& obj) +template <> +EventPtr _impl::doMakeEvent<Event>(const QJsonObject& obj) { // Check more specific event types first - if (auto e = RoomEvent::fromJson(obj)) - return e; + if (auto e = doMakeEvent<RoomEvent>(obj)) + return EventPtr(move(e)); - return makeIfMatches<Event, - TypingEvent, ReceiptEvent>(obj, obj["type"].toString()); + return EventPtr { makeIfMatches<Event, + TypingEvent, ReceiptEvent>(obj, obj["type"].toString()) }; } +RoomEvent::RoomEvent(Event::Type type) : Event(type) { } + RoomEvent::RoomEvent(Type type, const QJsonObject& rep) : Event(type, rep), _id(rep["event_id"].toString()) - , _serverTimestamp( - QMatrixClient::fromJson<QDateTime>(rep["origin_server_ts"])) , _roomId(rep["room_id"].toString()) , _senderId(rep["sender"].toString()) - , _txnId(rep["unsigned"].toObject().value("transactionId").toString()) + , _serverTimestamp( + QMatrixClient::fromJson<QDateTime>(rep["origin_server_ts"])) { // if (_id.isEmpty()) // { @@ -103,20 +109,38 @@ RoomEvent::RoomEvent(Type type, const QJsonObject& rep) // qCWarning(EVENTS) << "Can't find sender in a room event"; // qCWarning(EVENTS) << formatJson << rep; // } + auto unsignedData = rep["unsigned"].toObject(); + auto redaction = unsignedData.value("redacted_because"); + if (redaction.isObject()) + { + _redactedBecause.reset(new RedactionEvent(redaction.toObject())); + return; + } + + _txnId = unsignedData.value("transactionId").toString(); if (!_txnId.isEmpty()) qCDebug(EVENTS) << "Event transactionId:" << _txnId; } +RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job + +QString RoomEvent::redactionReason() const +{ + return isRedacted() ? _redactedBecause->reason() : QString{}; +} + void RoomEvent::addId(const QString& id) { Q_ASSERT(_id.isEmpty()); Q_ASSERT(!id.isEmpty()); _id = id; } -RoomEvent* RoomEvent::fromJson(const QJsonObject& obj) +template <> +RoomEventPtr _impl::doMakeEvent(const QJsonObject& obj) { - return makeIfMatches<RoomEvent, + return RoomEventPtr { makeIfMatches<RoomEvent, RoomMessageEvent, RoomNameEvent, RoomAliasesEvent, RoomCanonicalAliasEvent, RoomMemberEvent, RoomTopicEvent, - RoomAvatarEvent, EncryptionEvent>(obj, obj["type"].toString()); + RoomAvatarEvent, EncryptionEvent, RedactionEvent> + (obj, obj["type"].toString()) }; } diff --git a/events/event.h b/events/event.h index cc99b57b..d4923f1f 100644 --- a/events/event.h +++ b/events/event.h @@ -25,8 +25,19 @@ #include "util.h" +#include <memory> + namespace QMatrixClient { + template <typename EventT> + using event_ptr_tt = std::unique_ptr<EventT>; + + namespace _impl + { + template <typename EventT> + event_ptr_tt<EventT> doMakeEvent(const QJsonObject& obj); + } + class Event { Q_GADGET @@ -37,17 +48,19 @@ namespace QMatrixClient Typing, Receipt, RoomEventBase = 0x1000, RoomMessage = RoomEventBase + 1, - RoomEncryptedMessage, + RoomEncryptedMessage, Redaction, RoomStateEventBase = 0x1800, RoomName = RoomStateEventBase + 1, RoomAliases, RoomCanonicalAlias, RoomMember, RoomTopic, - RoomAvatar, RoomEncryption, + RoomAvatar, RoomEncryption, RoomCreate, RoomJoinRules, + RoomPowerLevels, Reserved = 0x2000 }; explicit Event(Type type) : _type(type) { } Event(Type type, const QJsonObject& rep); Event(const Event&) = delete; + virtual ~Event(); Type type() const { return _type; } bool isStateEvent() const @@ -63,12 +76,6 @@ namespace QMatrixClient // (and in most cases it will be a combination of other fields // instead of "content" field). - /** Create an event with proper type from a JSON object - * Use this factory to detect the type from the JSON object contents - * and create an event object of that type. - */ - static Event* fromJson(const QJsonObject& obj); - protected: const QJsonObject contentJson() const; @@ -81,28 +88,67 @@ namespace QMatrixClient Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) }; using EventType = Event::Type; + using EventPtr = event_ptr_tt<Event>; + /** 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 EventT> - class EventsBatch : public std::vector<EventT*> + event_ptr_tt<EventT> makeEvent(const QJsonObject& obj) + { + auto e = _impl::doMakeEvent<EventT>(obj); + if (!e) + e.reset(new EventT(EventType::Unknown, obj)); + return e; + } + + namespace _impl + { + template <> + EventPtr doMakeEvent<Event>(const QJsonObject& obj); + } + + /** + * \brief A vector of pointers to events with deserialisation capabilities + * + * This is a simple wrapper over a generic vector type that adds + * a convenience method to deserialise events from QJsonArray. + * \tparam EventT base type of all events in the vector + */ + template <typename EventT> + class EventsBatch : public std::vector<event_ptr_tt<EventT>> { public: + /** + * \brief Deserialise events from an array + * + * Given the following JSON construct, creates events from + * the array stored at key "node": + * \code + * "container": { + * "node": [ { "event_id": "!evt1:srv.org", ... }, ... ] + * } + * \endcode + * \param container - the wrapping JSON object + * \param node - the key in container that holds the array of events + */ void fromJson(const QJsonObject& container, const QString& node) { const auto objs = container.value(node).toArray(); - using size_type = typename std::vector<EventT*>::size_type; + using size_type = typename std::vector<event_ptr_tt<EventT>>::size_type; // The below line accommodates the difference in size types of // STL and Qt containers. this->reserve(static_cast<size_type>(objs.size())); for (auto objValue: objs) - { - const auto o = objValue.toObject(); - auto e = EventT::fromJson(o); - this->push_back(e ? e : new EventT(EventType::Unknown, o)); - } + this->emplace_back(makeEvent<EventT>(objValue.toObject())); } }; using Events = EventsBatch<Event>; + class RedactionEvent; + /** This class corresponds to m.room.* events */ class RoomEvent : public Event { @@ -111,15 +157,26 @@ namespace QMatrixClient Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT) Q_PROPERTY(QString roomId READ roomId CONSTANT) Q_PROPERTY(QString senderId READ senderId CONSTANT) - Q_PROPERTY(QString transactionId READ transactionId CONSTANT) + Q_PROPERTY(QString redactionReason READ redactionReason) + Q_PROPERTY(bool isRedacted READ isRedacted) + Q_PROPERTY(QString transactionId READ transactionId) public: - explicit RoomEvent(Type type) : Event(type) { } + // RedactionEvent is an incomplete type here so we cannot inline + // constructors and destructors + explicit RoomEvent(Type type); RoomEvent(Type type, const QJsonObject& rep); + ~RoomEvent(); const QString& id() const { return _id; } const QDateTime& timestamp() const { return _serverTimestamp; } const QString& roomId() const { return _roomId; } const QString& senderId() const { return _senderId; } + bool isRedacted() const { return bool(_redactedBecause); } + const RedactionEvent* redactedBecause() const + { + return _redactedBecause.get(); + } + QString redactionReason() const; const QString& transactionId() const { return _txnId; } /** @@ -143,17 +200,45 @@ namespace QMatrixClient */ void addId(const QString& id); - // "Static override" of the one in Event - static RoomEvent* fromJson(const QJsonObject& obj); - private: QString _id; - QDateTime _serverTimestamp; QString _roomId; QString _senderId; + QDateTime _serverTimestamp; + event_ptr_tt<RedactionEvent> _redactedBecause; QString _txnId; }; using RoomEvents = EventsBatch<RoomEvent>; + using RoomEventPtr = event_ptr_tt<RoomEvent>; + + namespace _impl + { + template <> + RoomEventPtr doMakeEvent<RoomEvent>(const QJsonObject& obj); + } + + /** + * Conceptually similar to QStringView (but much more primitive), it's a + * simple abstraction over a pair of RoomEvents::const_iterator values + * referring to the beginning and the end of a range in a RoomEvents + * container. + */ + struct RoomEventsRange + { + RoomEvents::iterator from; + RoomEvents::iterator to; + + RoomEvents::size_type size() const + { + Q_ASSERT(std::distance(from, to) >= 0); + return RoomEvents::size_type(std::distance(from, to)); + } + bool empty() const { return from == to; } + RoomEvents::const_iterator begin() const { return from; } + RoomEvents::const_iterator end() const { return to; } + RoomEvents::iterator begin() { return from; } + RoomEvents::iterator end() { return to; } + }; template <typename ContentT> class StateEvent: public RoomEvent diff --git a/events/receiptevent.cpp b/events/receiptevent.cpp index b36ddb23..e30fe4e4 100644 --- a/events/receiptevent.cpp +++ b/events/receiptevent.cpp @@ -56,15 +56,15 @@ ReceiptEvent::ReceiptEvent(const QJsonObject& obj) continue; } const QJsonObject reads = eventIt.value().toObject().value("m.read").toObject(); - std::vector<Receipt> receipts; - receipts.reserve(static_cast<size_t>(reads.size())); + QVector<Receipt> receipts; + receipts.reserve(reads.size()); for( auto userIt = reads.begin(); userIt != reads.end(); ++userIt ) { const QJsonObject user = userIt.value().toObject(); receipts.push_back({userIt.key(), QMatrixClient::fromJson<QDateTime>(user["ts"])}); } - _eventsWithReceipts.push_back({eventIt.key(), receipts}); + _eventsWithReceipts.push_back({eventIt.key(), std::move(receipts)}); } static const auto UnreadMsgsKey = QStringLiteral("x-qmatrixclient.unread_messages"); diff --git a/events/receiptevent.h b/events/receiptevent.h index 15fdf946..9494c7c6 100644 --- a/events/receiptevent.h +++ b/events/receiptevent.h @@ -30,9 +30,9 @@ namespace QMatrixClient struct ReceiptsForEvent { QString evtId; - std::vector<Receipt> receipts; + QVector<Receipt> receipts; }; - using EventsWithReceipts = std::vector<ReceiptsForEvent>; + using EventsWithReceipts = QVector<ReceiptsForEvent>; class ReceiptEvent: public Event { diff --git a/events/redactionevent.cpp b/events/redactionevent.cpp new file mode 100644 index 00000000..bf467718 --- /dev/null +++ b/events/redactionevent.cpp @@ -0,0 +1 @@ +#include "redactionevent.h" diff --git a/events/redactionevent.h b/events/redactionevent.h new file mode 100644 index 00000000..fa6902ab --- /dev/null +++ b/events/redactionevent.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * 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 "event.h" + +namespace QMatrixClient +{ + class RedactionEvent : public RoomEvent + { + public: + static constexpr const char* const TypeId = "m.room.redaction"; + + RedactionEvent(const QJsonObject& obj) + : RoomEvent(Type::Redaction, obj) + , _redactedEvent(obj.value("redacts").toString()) + , _reason(contentJson().value("reason").toString()) + { } + + const QString& redactedEvent() const { return _redactedEvent; } + const QString& reason() const { return _reason; } + + private: + QString _redactedEvent; + QString _reason; + }; +} // namespace QMatrixClient diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp index f06474e9..bc41abf6 100644 --- a/events/roommessageevent.cpp +++ b/events/roommessageevent.cpp @@ -58,7 +58,6 @@ QString msgTypeToJson(MsgType enumType) if (it != msgTypes.end()) return it->jsonType; - qCCritical(EVENTS) << "Unknown msgtype:" << enumType; return {}; } @@ -69,8 +68,7 @@ MsgType jsonToMsgType(const QString& jsonType) if (it != msgTypes.end()) return it->enumType; - qCCritical(EVENTS) << "Unknown msgtype:" << jsonType; - return {}; + return MsgType::Unknown; } RoomMessageEvent::RoomMessageEvent(const QString& plainBody, @@ -81,6 +79,8 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody, RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) : RoomEvent(Type::RoomMessage, obj), _content(nullptr) { + if (isRedacted()) + return; const QJsonObject content = contentJson(); if ( content.contains("msgtype") && content.contains("body") ) { |