diff options
author | Hubert Chathi <uhoreg@debian.org> | 2020-04-20 18:01:38 -0400 |
---|---|---|
committer | Hubert Chathi <uhoreg@debian.org> | 2020-04-20 18:01:38 -0400 |
commit | 035d428becd62869b88793bb48d59410056b1f31 (patch) | |
tree | 0cd837955fa1037c74ee524a089ff1b788b75861 /lib/events | |
parent | 688dc682e0c6ae12dc87d781ffe53c974dad60df (diff) | |
parent | d8e1f4ee70902e2b9e5e9c699423d7f3fafe6238 (diff) | |
download | libquotient-035d428becd62869b88793bb48d59410056b1f31.tar.gz libquotient-035d428becd62869b88793bb48d59410056b1f31.zip |
Update upstream source from tag 'upstream/0.5.3.2'
Update to upstream version '0.5.3.2'
with Debian dir 49a325a077f4a2ebcd5b73adf3782a6fb5ecb3fe
Diffstat (limited to 'lib/events')
-rw-r--r-- | lib/events/accountdataevents.h | 1 | ||||
-rw-r--r-- | lib/events/reactionevent.cpp | 44 | ||||
-rw-r--r-- | lib/events/reactionevent.h | 81 | ||||
-rw-r--r-- | lib/events/roomevent.cpp | 14 | ||||
-rw-r--r-- | lib/events/roomevent.h | 2 | ||||
-rw-r--r-- | lib/events/roommessageevent.cpp | 108 | ||||
-rw-r--r-- | lib/events/roommessageevent.h | 3 |
7 files changed, 226 insertions, 27 deletions
diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index a99d85ac..a43e358c 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -28,6 +28,7 @@ namespace QMatrixClient { constexpr const char* FavouriteTag = "m.favourite"; constexpr const char* LowPriorityTag = "m.lowpriority"; + constexpr const char* ServerNoticeTag = "m.server_notice"; struct TagRecord { diff --git a/lib/events/reactionevent.cpp b/lib/events/reactionevent.cpp new file mode 100644 index 00000000..0081edc2 --- /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..7a4c9b5a --- /dev/null +++ b/lib/events/reactionevent.h @@ -0,0 +1,81 @@ +/****************************************************************************** + * 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 { + // 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"; } + 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 EventRelation(Reply(), std::move(eventId)); + } + static EventRelation annotate(QString eventId, QString key) + { + return EventRelation(Annotation(), std::move(eventId), std::move(key)); + } + static EventRelation replace(QString eventId) + { + return EventRelation(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 3d03509f..5e2d0b3c 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -66,6 +66,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 ce96174e..8926ab0f 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -51,6 +51,8 @@ namespace QMatrixClient 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 8f4e0ebc..1edf82e4 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -30,12 +30,13 @@ 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 BodyKeyL = "body"_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 +50,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; } @@ -62,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> } @@ -95,12 +96,27 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, const QString& jsonMsgType, 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(BodyKeyL, plainBody); + newContentJson.insert(MsgTypeKeyL, jsonMsgType); + json.insert(QStringLiteral("m.new_content"), newContentJson); + json[MsgTypeKeyL] = jsonMsgType; + json[BodyKeyL] = "* " + plainBody; + return json; + } + } } json.insert(QStringLiteral("msgtype"), jsonMsgType); json.insert(QStringLiteral("body"), plainBody); @@ -159,9 +175,9 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) if (isRedacted()) return; const QJsonObject content = contentJson(); - if ( content.contains(MsgTypeKey) && content.contains(BodyKey) ) + if ( content.contains(MsgTypeKeyL) && content.contains(BodyKeyL) ) { - auto msgtype = content[MsgTypeKey].toString(); + auto msgtype = content[MsgTypeKeyL].toString(); bool msgTypeFound = false; for (const auto& mt: msgTypes) if (mt.matrixType == msgtype) @@ -191,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 @@ -223,6 +239,16 @@ 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(); @@ -251,41 +277,69 @@ TextContent::TextContent(const QString& text, const QString& contentType, mimeType = QMimeDatabase().mimeTypeForName("text/html"); } +namespace QMatrixClient +{ +Omittable<RelatesTo> relationFromJson(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() }; +} +} + TextContent::TextContent(const QJsonObject& json) + : relatesTo(relationFromJson(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 RichBodyKey = 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(RichBodyKey, body); } - if (!relatesTo.omitted()) + if (!relatesTo.omitted()) { json->insert(QStringLiteral("m.relates_to"), - QJsonObject { { relatesTo->type, relatesTo->eventId } }); + QJsonObject { { relatesTo->type, relatesTo->eventId } }); + if (relatesTo->type == RelatesTo::ReplacementTypeId()) { + QJsonObject newContentJson; + if (mimeType.inherits("text/html")) { + json->insert(FormatKey, HtmlContentTypeId); + json->insert(RichBodyKey, 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 c2e075eb..7320e4ea 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -72,6 +72,7 @@ namespace QMatrixClient bool hasTextContent() const; bool hasFileContent() const; bool hasThumbnail() const; + QString replacedEvent() const; static QString rawMsgTypeForUrl(const QUrl& url); static QString rawMsgTypeForFile(const QFileInfo& fi); @@ -79,6 +80,7 @@ namespace QMatrixClient private: QScopedPointer<EventContent::TypedBase> _content; + // FIXME: should it really be static? static QJsonObject assembleContentJson(const QString& plainBody, const QString& jsonMsgType, EventContent::TypedBase* content); @@ -95,6 +97,7 @@ namespace QMatrixClient 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; }; |