aboutsummaryrefslogtreecommitdiff
path: root/lib/events
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2019-08-02 09:32:49 +0900
committerKitsune Ral <Kitsune-Ral@users.sf.net>2019-08-02 09:32:49 +0900
commit35cfef7a4253d49a37e5ce21c337fbb3d2633c42 (patch)
treedfcc77c4e404b6b8f07a4c9ee6283e70e4723883 /lib/events
parent5b37e15d6a57d3b689c88f5cfce7afea9787a034 (diff)
parent5b236dfe895c7766002559570aa29c9033009228 (diff)
downloadlibquotient-35cfef7a4253d49a37e5ce21c337fbb3d2633c42.tar.gz
libquotient-35cfef7a4253d49a37e5ce21c337fbb3d2633c42.zip
Merge branch 'master' into use-clang-format
Diffstat (limited to 'lib/events')
-rw-r--r--lib/events/encryptedevent.cpp32
-rw-r--r--lib/events/encryptedevent.h68
-rw-r--r--lib/events/encryptionevent.cpp18
-rw-r--r--lib/events/event.h8
-rw-r--r--lib/events/reactionevent.cpp44
-rw-r--r--lib/events/reactionevent.h79
-rw-r--r--lib/events/roomevent.cpp14
-rw-r--r--lib/events/roomevent.h2
-rw-r--r--lib/events/roommessageevent.cpp107
-rw-r--r--lib/events/roommessageevent.h3
10 files changed, 338 insertions, 37 deletions
diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp
new file mode 100644
index 00000000..dac245fa
--- /dev/null
+++ b/lib/events/encryptedevent.cpp
@@ -0,0 +1,32 @@
+#include "encryptedevent.h"
+
+#include "room.h"
+
+using namespace QMatrixClient;
+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(EVENTS) << "Encrypted event" << id();
+}
diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h
new file mode 100644
index 00000000..0dbce25c
--- /dev/null
+++ b/lib/events/encryptedevent.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "e2ee.h"
+#include "roomevent.h"
+
+namespace QMatrixClient
+{
+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 QMatrixClient
diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp
index 6aa7063b..995c8dad 100644
--- a/lib/events/encryptionevent.cpp
+++ b/lib/events/encryptionevent.cpp
@@ -6,12 +6,14 @@
#include "encryptionevent.h"
#include "converters.h"
+#include "e2ee.h"
#include "logging.h"
#include <array>
-static const std::array<QString, 1> encryptionStrings = { { QStringLiteral(
- "m.megolm.v1.aes-sha2") } };
+static const std::array<QString, 1> encryptionStrings = {
+ { QMatrixClient::MegolmV1AesSha2AlgoKey }
+};
namespace QMatrixClient
{
@@ -36,9 +38,9 @@ using namespace QMatrixClient;
EncryptionEventContent::EncryptionEventContent(const QJsonObject& json)
: encryption(fromJson<EncryptionType>(json["algorithm"_ls]))
- , algorithm(sanitized(json["algorithm"_ls].toString()))
- , rotationPeriodMs(json["rotation_period_ms"_ls].toInt(604800000))
- , rotationPeriodMsgs(json["rotation_period_msgs"_ls].toInt(100))
+ , algorithm(sanitized(json[AlgorithmKeyL].toString()))
+ , rotationPeriodMs(json[RotationPeriodMsKeyL].toInt(604800000))
+ , rotationPeriodMsgs(json[RotationPeriodMsgsKeyL].toInt(100))
{}
void EncryptionEventContent::fillJson(QJsonObject* o) const
@@ -48,7 +50,7 @@ void EncryptionEventContent::fillJson(QJsonObject* o) const
encryption != EncryptionType::Undefined, __FUNCTION__,
"The key 'algorithm' must be explicit in EncryptionEventContent");
if (encryption != EncryptionType::Undefined)
- o->insert(QStringLiteral("algorithm"), algorithm);
- o->insert(QStringLiteral("rotation_period_ms"), rotationPeriodMs);
- o->insert(QStringLiteral("rotation_period_msgs"), rotationPeriodMsgs);
+ o->insert(AlgorithmKey, algorithm);
+ o->insert(RotationPeriodMsKey, rotationPeriodMs);
+ o->insert(RotationPeriodMsgsKey, rotationPeriodMsgs);
}
diff --git a/lib/events/event.h b/lib/events/event.h
index 8056ccbe..d6525281 100644
--- a/lib/events/event.h
+++ b/lib/events/event.h
@@ -58,11 +58,13 @@ ptrCast(event_ptr_tt<SourceT>&& ptr)
// === 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;
@@ -391,10 +393,8 @@ 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 = {})
+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))
diff --git a/lib/events/reactionevent.cpp b/lib/events/reactionevent.cpp
new file mode 100644
index 00000000..0a080607
--- /dev/null
+++ b/lib/events/reactionevent.cpp
@@ -0,0 +1,44 @@
+/******************************************************************************
+ * Copyright (C) 2019 Kitsune Ral <kitsune-ral@users.sf.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "reactionevent.h"
+
+using namespace QMatrixClient;
+
+void QMatrixClient::JsonObjectConverter<EventRelation>::dumpTo(
+ QJsonObject& jo, const EventRelation& pod)
+{
+ if (pod.type.isEmpty()) {
+ qCWarning(MAIN) << "Empty relation type; won't dump to JSON";
+ return;
+ }
+ jo.insert(QStringLiteral("rel_type"), pod.type);
+ jo.insert(EventIdKey, pod.eventId);
+ if (pod.type == EventRelation::Annotation())
+ jo.insert(QStringLiteral("key"), pod.key);
+}
+
+void QMatrixClient::JsonObjectConverter<EventRelation>::fillFrom(
+ const QJsonObject& jo, EventRelation& pod)
+{
+ // The experimental logic for generic relationships (MSC1849)
+ fromJson(jo["rel_type"_ls], pod.type);
+ fromJson(jo[EventIdKeyL], pod.eventId);
+ if (pod.type == EventRelation::Annotation())
+ fromJson(jo["key"_ls], pod.key);
+}
diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h
new file mode 100644
index 00000000..d524b549
--- /dev/null
+++ b/lib/events/reactionevent.h
@@ -0,0 +1,79 @@
+/******************************************************************************
+ * Copyright (C) 2019 Kitsune Ral <kitsune-ral@users.sf.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include "roomevent.h"
+
+namespace QMatrixClient
+{
+
+struct EventRelation
+{
+ using reltypeid_t = const char*;
+ static constexpr reltypeid_t Reply() { return "m.in_reply_to"; }
+ static constexpr reltypeid_t Annotation() { return "m.annotation"; }
+ static constexpr reltypeid_t Replacement() { return "m.replace"; }
+
+ QString type;
+ QString eventId;
+ QString key = {}; // Only used for m.annotation for now
+
+ static EventRelation replyTo(QString eventId)
+ {
+ return { Reply(), std::move(eventId) };
+ }
+ static EventRelation annotate(QString eventId, QString key)
+ {
+ return { Annotation(), std::move(eventId), std::move(key) };
+ }
+ static EventRelation replace(QString eventId)
+ {
+ return { Replacement(), std::move(eventId) };
+ }
+};
+template <>
+struct JsonObjectConverter<EventRelation>
+{
+ static void dumpTo(QJsonObject& jo, const EventRelation& pod);
+ static void fillFrom(const QJsonObject& jo, EventRelation& pod);
+};
+
+class ReactionEvent : public RoomEvent
+{
+public:
+ DEFINE_EVENT_TYPEID("m.reaction", ReactionEvent)
+
+ explicit ReactionEvent(const EventRelation& value)
+ : RoomEvent(typeId(), matrixTypeId(),
+ { { QStringLiteral("m.relates_to"), toJson(value) } })
+ {}
+ explicit ReactionEvent(const QJsonObject& obj)
+ : RoomEvent(typeId(), obj)
+ {}
+ EventRelation relation() const
+ {
+ return content<EventRelation>(QStringLiteral("m.relates_to"));
+ }
+
+private:
+ EventRelation _relation;
+};
+REGISTER_EVENT_TYPE(ReactionEvent)
+
+} // namespace QMatrixClient
diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp
index 513a99d0..fb715473 100644
--- a/lib/events/roomevent.cpp
+++ b/lib/events/roomevent.cpp
@@ -62,6 +62,20 @@ QString RoomEvent::senderId() const
return fullJson()["sender"_ls].toString();
}
+bool RoomEvent::isReplaced() const
+{
+ return unsignedJson()["m.relations"_ls].toObject().contains("m.replace");
+}
+
+QString RoomEvent::replacedBy() const
+{
+ // clang-format off
+ return unsignedJson()["m.relations"_ls].toObject()
+ .value("m.replace").toObject()
+ .value(EventIdKeyL).toString();
+ // clang-format on
+}
+
QString RoomEvent::redactionReason() const
{
return isRedacted() ? _redactedBecause->reason() : QString {};
diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h
index dd0d25eb..8edb397c 100644
--- a/lib/events/roomevent.h
+++ b/lib/events/roomevent.h
@@ -51,6 +51,8 @@ public:
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
{
diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp
index c7f17303..da8d59ca 100644
--- a/lib/events/roommessageevent.cpp
+++ b/lib/events/roommessageevent.cpp
@@ -30,12 +30,12 @@ using namespace EventContent;
using MsgType = RoomMessageEvent::MsgType;
-static const auto RelatesToKey = "m.relates_to"_ls;
-static const auto MsgTypeKey = "msgtype"_ls;
-static const auto BodyKey = "body"_ls;
-static const auto FormattedBodyKey = "formatted_body"_ls;
+static const auto RelatesToKeyL = "m.relates_to"_ls;
+static const auto MsgTypeKeyL = "msgtype"_ls;
+static const auto FormattedBodyKeyL = "formatted_body"_ls;
static const auto TextTypeKey = "m.text";
+static const auto EmoteTypeKey = "m.emote";
static const auto NoticeTypeKey = "m.notice";
static const auto HtmlContentTypeId = QStringLiteral("org.matrix.custom.html");
@@ -49,7 +49,7 @@ TypedBase* make(const QJsonObject& json)
template <>
TypedBase* make<TextContent>(const QJsonObject& json)
{
- return json.contains(FormattedBodyKey) || json.contains(RelatesToKey)
+ return json.contains(FormattedBodyKeyL) || json.contains(RelatesToKeyL)
? new TextContent(json)
: nullptr;
}
@@ -63,7 +63,7 @@ struct MsgTypeDesc
const std::vector<MsgTypeDesc> msgTypes = {
{ TextTypeKey, MsgType::Text, make<TextContent> },
- { QStringLiteral("m.emote"), MsgType::Emote, 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> },
@@ -101,11 +101,25 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody,
TypedBase* content)
{
auto json = content ? content->toJson() : QJsonObject();
- if (jsonMsgType != TextTypeKey && jsonMsgType != NoticeTypeKey
- && json.contains(RelatesToKey)) {
- json.remove(RelatesToKey);
- qCWarning(EVENTS) << RelatesToKey << "cannot be used in" << jsonMsgType
- << "messages; the relation has been stripped off";
+ if (json.contains(RelatesToKeyL)) {
+ if (jsonMsgType != TextTypeKey && jsonMsgType != NoticeTypeKey
+ && jsonMsgType != EmoteTypeKey) {
+ json.remove(RelatesToKeyL);
+ qCWarning(EVENTS)
+ << RelatesToKeyL << "cannot be used in" << jsonMsgType
+ << "messages; the relation has been stripped off";
+ } else {
+ // After the above, we know for sure that the content is TextContent
+ // and that its RelatesTo structure is not omitted
+ auto* textContent = static_cast<const TextContent*>(content);
+ if (textContent->relatesTo->type == RelatesTo::ReplacementTypeId()) {
+ auto newContentJson = json.take("m.new_content"_ls).toObject();
+ newContentJson.insert(BodyKey, plainBody);
+ newContentJson.insert(TypeKey, jsonMsgType);
+ json.insert(QStringLiteral("m.new_content"), newContentJson);
+ json[BodyKeyL] = "* " + plainBody;
+ }
+ }
}
json.insert(QStringLiteral("msgtype"), jsonMsgType);
json.insert(QStringLiteral("body"), plainBody);
@@ -166,8 +180,8 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj)
if (isRedacted())
return;
const QJsonObject content = contentJson();
- if (content.contains(MsgTypeKey) && content.contains(BodyKey)) {
- auto msgtype = content[MsgTypeKey].toString();
+ if (content.contains(MsgTypeKeyL) && content.contains(BodyKeyL)) {
+ auto msgtype = content[MsgTypeKeyL].toString();
bool msgTypeFound = false;
for (const auto& mt : msgTypes)
if (mt.matrixType == msgtype) {
@@ -193,12 +207,12 @@ RoomMessageEvent::MsgType RoomMessageEvent::msgtype() const
QString RoomMessageEvent::rawMsgtype() const
{
- return contentJson()[MsgTypeKey].toString();
+ return contentJson()[MsgTypeKeyL].toString();
}
QString RoomMessageEvent::plainBody() const
{
- return contentJson()[BodyKey].toString();
+ return contentJson()[BodyKeyL].toString();
}
QMimeType RoomMessageEvent::mimeType() const
@@ -225,6 +239,17 @@ bool RoomMessageEvent::hasThumbnail() const
return content() && content()->thumbnailInfo();
}
+QString RoomMessageEvent::replacedEvent() const
+{
+ if (!content() || !hasTextContent())
+ return {};
+
+ const auto& rel = static_cast<const TextContent*>(content())->relatesTo;
+ return !rel.omitted() && rel->type == RelatesTo::ReplacementTypeId()
+ ? rel->eventId
+ : QString();
+}
+
QString rawMsgTypeForMimeType(const QMimeType& mimeType)
{
auto name = mimeType.name();
@@ -256,39 +281,71 @@ TextContent::TextContent(const QString& text, const QString& contentType,
mimeType = QMimeDatabase().mimeTypeForName("text/html");
}
+namespace QMatrixClient
+{
+// Overload the default fromJson<> logic that defined in converters.h
+// as we want
+template <>
+Omittable<RelatesTo> fromJson(const QJsonValue& jv)
+{
+ const auto jo = jv.toObject();
+ if (jo.isEmpty())
+ return none;
+ const auto replyJson = jo.value(RelatesTo::ReplyTypeId()).toObject();
+ if (!replyJson.isEmpty())
+ return replyTo(fromJson<QString>(replyJson[EventIdKeyL]));
+
+ return RelatesTo { jo.value("rel_type"_ls).toString(),
+ jo.value(EventIdKeyL).toString() };
+}
+} // namespace QMatrixClient
+
TextContent::TextContent(const QJsonObject& json)
+ : 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();
// Special-casing the custom matrix.org's (actually, Riot's) way
// of sending HTML messages.
- if (json["format"_ls].toString() == HtmlContentTypeId) {
+ if (actualJson["format"_ls].toString() == HtmlContentTypeId) {
mimeType = HtmlMimeType;
- body = json[FormattedBodyKey].toString();
+ body = actualJson[FormattedBodyKeyL].toString();
} else {
// Falling back to plain text, as there's no standard way to describe
// rich text in messages.
mimeType = PlainTextMimeType;
- body = json[BodyKey].toString();
+ body = actualJson[BodyKeyL].toString();
}
- const auto replyJson =
- json[RelatesToKey].toObject().value(RelatesTo::ReplyTypeId()).toObject();
- if (!replyJson.isEmpty())
- relatesTo = replyTo(fromJson<QString>(replyJson[EventIdKeyL]));
}
void TextContent::fillJson(QJsonObject* json) const
{
+ static const auto FormatKey = QStringLiteral("format");
+ static const auto FormattedBodyKey = QStringLiteral("formatted_body");
+
Q_ASSERT(json);
if (mimeType.inherits("text/html")) {
- json->insert(QStringLiteral("format"), HtmlContentTypeId);
- json->insert(QStringLiteral("formatted_body"), body);
+ json->insert(FormatKey, HtmlContentTypeId);
+ json->insert(FormattedBodyKey, body);
}
- if (!relatesTo.omitted())
+ if (!relatesTo.omitted()) {
json->insert(QStringLiteral("m.relates_to"),
QJsonObject { { relatesTo->type, relatesTo->eventId } });
+ if (relatesTo->type == RelatesTo::ReplacementTypeId()) {
+ QJsonObject newContentJson;
+ if (mimeType.inherits("text/html")) {
+ json->insert(FormatKey, HtmlContentTypeId);
+ json->insert(FormattedBodyKey, body);
+ }
+ json->insert(QStringLiteral("m.new_content"), newContentJson);
+ }
+ }
}
LocationContent::LocationContent(const QString& geoUri,
diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h
index eabb21e3..1f1fde41 100644
--- a/lib/events/roommessageevent.h
+++ b/lib/events/roommessageevent.h
@@ -77,6 +77,7 @@ public:
bool hasTextContent() const;
bool hasFileContent() const;
bool hasThumbnail() const;
+ QString replacedEvent() const;
static QString rawMsgTypeForUrl(const QUrl& url);
static QString rawMsgTypeForFile(const QFileInfo& fi);
@@ -84,6 +85,7 @@ public:
private:
QScopedPointer<EventContent::TypedBase> _content;
+ // FIXME: should it really be static?
static QJsonObject assembleContentJson(const QString& plainBody,
const QString& jsonMsgType,
EventContent::TypedBase* content);
@@ -101,6 +103,7 @@ namespace EventContent
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;
};