aboutsummaryrefslogtreecommitdiff
path: root/events
diff options
context:
space:
mode:
Diffstat (limited to 'events')
-rw-r--r--events/event.cpp48
-rw-r--r--events/event.h127
-rw-r--r--events/receiptevent.cpp6
-rw-r--r--events/receiptevent.h4
-rw-r--r--events/redactionevent.cpp1
-rw-r--r--events/redactionevent.h43
-rw-r--r--events/roommessageevent.cpp6
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") )
{