aboutsummaryrefslogtreecommitdiff
path: root/lib/events
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2021-08-10 08:18:17 +0200
committerAlexey Rusakov <Kitsune-Ral@users.sf.net>2021-10-10 20:46:01 +0200
commit01103222dc59526b37d594b93b0b1cd7473e3f6f (patch)
tree8e3b679377571fd9b9014afbe62eeba6249d42e2 /lib/events
parent4f08c88d234119c2a76874ebd2b1433b81992427 (diff)
parent7b516cdf0b987e542b1e4cd4556ecb2bfbde3ff9 (diff)
downloadlibquotient-01103222dc59526b37d594b93b0b1cd7473e3f6f.tar.gz
libquotient-01103222dc59526b37d594b93b0b1cd7473e3f6f.zip
Merge branch 'master' into kitsune-fix-read-receipts-and-markers
Diffstat (limited to 'lib/events')
-rw-r--r--lib/events/encryptedfile.h88
-rw-r--r--lib/events/encryptionevent.cpp8
-rw-r--r--lib/events/encryptionevent.h4
-rw-r--r--lib/events/eventcontent.cpp20
-rw-r--r--lib/events/eventcontent.h17
-rw-r--r--lib/events/roomavatarevent.h2
-rw-r--r--lib/events/roomcreateevent.cpp21
-rw-r--r--lib/events/roomcreateevent.h2
-rw-r--r--lib/events/roomevent.h11
-rw-r--r--lib/events/roommessageevent.cpp8
-rw-r--r--lib/events/roommessageevent.h17
-rw-r--r--lib/events/stateevent.h4
12 files changed, 177 insertions, 25 deletions
diff --git a/lib/events/encryptedfile.h b/lib/events/encryptedfile.h
new file mode 100644
index 00000000..24ac9de1
--- /dev/null
+++ b/lib/events/encryptedfile.h
@@ -0,0 +1,88 @@
+// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
+//
+// SPDX-License-Identifier: LGPl-2.1-or-later
+
+#pragma once
+
+#include "converters.h"
+
+namespace Quotient {
+/**
+ * JSON Web Key object as specified in
+ * https://spec.matrix.org/unstable/client-server-api/#extensions-to-mroommessage-msgtypes
+ * The only currently relevant member is `k`, the rest needs to be set to the defaults specified in the spec.
+ */
+struct JWK
+{
+ Q_GADGET
+ Q_PROPERTY(QString kty MEMBER kty CONSTANT)
+ Q_PROPERTY(QStringList keyOps MEMBER keyOps CONSTANT)
+ Q_PROPERTY(QString alg MEMBER alg CONSTANT)
+ Q_PROPERTY(QString k MEMBER k CONSTANT)
+ Q_PROPERTY(bool ext MEMBER ext CONSTANT)
+
+public:
+ QString kty;
+ QStringList keyOps;
+ QString alg;
+ QString k;
+ bool ext;
+};
+
+struct EncryptedFile
+{
+ Q_GADGET
+ Q_PROPERTY(QUrl url MEMBER url CONSTANT)
+ Q_PROPERTY(JWK key MEMBER key CONSTANT)
+ Q_PROPERTY(QString iv MEMBER iv CONSTANT)
+ Q_PROPERTY(QHash<QString, QString> hashes MEMBER hashes CONSTANT)
+ Q_PROPERTY(QString v MEMBER v CONSTANT)
+
+public:
+ QUrl url;
+ JWK key;
+ QString iv;
+ QHash<QString, QString> hashes;
+ QString v;
+};
+
+template <>
+struct JsonObjectConverter<EncryptedFile> {
+ static void dumpTo(QJsonObject& jo, const EncryptedFile& pod)
+ {
+ addParam<>(jo, QStringLiteral("url"), pod.url);
+ addParam<>(jo, QStringLiteral("key"), pod.key);
+ addParam<>(jo, QStringLiteral("iv"), pod.iv);
+ addParam<>(jo, QStringLiteral("hashes"), pod.hashes);
+ addParam<>(jo, QStringLiteral("v"), pod.v);
+ }
+ static void fillFrom(const QJsonObject& jo, EncryptedFile& pod)
+ {
+ fromJson(jo.value("url"_ls), pod.url);
+ fromJson(jo.value("key"_ls), pod.key);
+ fromJson(jo.value("iv"_ls), pod.iv);
+ fromJson(jo.value("hashes"_ls), pod.hashes);
+ fromJson(jo.value("v"_ls), pod.v);
+ }
+};
+
+template <>
+struct JsonObjectConverter<JWK> {
+ static void dumpTo(QJsonObject& jo, const JWK& pod)
+ {
+ addParam<>(jo, QStringLiteral("kty"), pod.kty);
+ addParam<>(jo, QStringLiteral("key_ops"), pod.keyOps);
+ addParam<>(jo, QStringLiteral("alg"), pod.alg);
+ addParam<>(jo, QStringLiteral("k"), pod.k);
+ addParam<>(jo, QStringLiteral("ext"), pod.ext);
+ }
+ static void fillFrom(const QJsonObject& jo, JWK& pod)
+ {
+ fromJson(jo.value("kty"_ls), pod.kty);
+ fromJson(jo.value("key_ops"_ls), pod.keyOps);
+ fromJson(jo.value("alg"_ls), pod.alg);
+ fromJson(jo.value("k"_ls), pod.k);
+ fromJson(jo.value("ext"_ls), pod.ext);
+ }
+};
+} // namespace Quotient
diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp
index 490a5e8a..aa05a96e 100644
--- a/lib/events/encryptionevent.cpp
+++ b/lib/events/encryptionevent.cpp
@@ -39,6 +39,14 @@ EncryptionEventContent::EncryptionEventContent(const QJsonObject& json)
, rotationPeriodMsgs(json[RotationPeriodMsgsKeyL].toInt(100))
{}
+EncryptionEventContent::EncryptionEventContent(EncryptionType et)
+ : encryption(et)
+{
+ if(encryption != Undefined) {
+ algorithm = encryptionStrings[encryption];
+ }
+}
+
void EncryptionEventContent::fillJson(QJsonObject* o) const
{
Q_ASSERT(o);
diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h
index 65ee4187..14439fcc 100644
--- a/lib/events/encryptionevent.h
+++ b/lib/events/encryptionevent.h
@@ -12,9 +12,7 @@ class EncryptionEventContent : public EventContent::Base {
public:
enum EncryptionType : size_t { MegolmV1AesSha2 = 0, Undefined };
- explicit EncryptionEventContent(EncryptionType et = Undefined)
- : encryption(et)
- {}
+ explicit EncryptionEventContent(EncryptionType et = Undefined);
explicit EncryptionEventContent(const QJsonObject& json);
EncryptionType encryption;
diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp
index 1f28f195..22878d4c 100644
--- a/lib/events/eventcontent.cpp
+++ b/lib/events/eventcontent.cpp
@@ -30,11 +30,12 @@ FileInfo::FileInfo(const QFileInfo &fi)
}
FileInfo::FileInfo(QUrl u, qint64 payloadSize, const QMimeType& mimeType,
- QString originalFilename)
+ Omittable<EncryptedFile> file, QString originalFilename)
: mimeType(mimeType)
, url(move(u))
, payloadSize(payloadSize)
, originalName(move(originalFilename))
+ , file(file)
{
if (!isValid())
qCWarning(MESSAGES)
@@ -44,6 +45,7 @@ FileInfo::FileInfo(QUrl u, qint64 payloadSize, const QMimeType& mimeType,
}
FileInfo::FileInfo(QUrl mxcUrl, const QJsonObject& infoJson,
+ const Omittable<EncryptedFile> &file,
QString originalFilename)
: originalInfoJson(infoJson)
, mimeType(
@@ -51,7 +53,11 @@ FileInfo::FileInfo(QUrl mxcUrl, const QJsonObject& infoJson,
, url(move(mxcUrl))
, payloadSize(fromJson<qint64>(infoJson["size"_ls]))
, originalName(move(originalFilename))
+ , file(file)
{
+ if(url.isEmpty() && file.has_value()) {
+ url = file->url;
+ }
if (!mimeType.isValid())
mimeType = QMimeDatabase().mimeTypeForData(QByteArray());
}
@@ -76,14 +82,15 @@ ImageInfo::ImageInfo(const QFileInfo& fi, QSize imageSize)
{}
ImageInfo::ImageInfo(const QUrl& mxcUrl, qint64 fileSize, const QMimeType& type,
- QSize imageSize, const QString& originalFilename)
- : FileInfo(mxcUrl, fileSize, type, originalFilename)
+ QSize imageSize, const Omittable<EncryptedFile> &file, const QString& originalFilename)
+ : FileInfo(mxcUrl, fileSize, type, file, originalFilename)
, imageSize(imageSize)
{}
ImageInfo::ImageInfo(const QUrl& mxcUrl, const QJsonObject& infoJson,
+ const Omittable<EncryptedFile> &file,
const QString& originalFilename)
- : FileInfo(mxcUrl, infoJson, originalFilename)
+ : FileInfo(mxcUrl, infoJson, file, originalFilename)
, imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt())
{}
@@ -96,9 +103,10 @@ void ImageInfo::fillInfoJson(QJsonObject* infoJson) const
infoJson->insert(QStringLiteral("h"), imageSize.height());
}
-Thumbnail::Thumbnail(const QJsonObject& infoJson)
+Thumbnail::Thumbnail(const QJsonObject& infoJson, const Omittable<EncryptedFile> &file)
: ImageInfo(QUrl(infoJson["thumbnail_url"_ls].toString()),
- infoJson["thumbnail_info"_ls].toObject())
+ infoJson["thumbnail_info"_ls].toObject(),
+ file)
{}
void Thumbnail::fillInfoJson(QJsonObject* infoJson) const
diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h
index 40ec3a49..f609a603 100644
--- a/lib/events/eventcontent.h
+++ b/lib/events/eventcontent.h
@@ -12,6 +12,8 @@
#include <QtCore/QUrl>
#include <QtCore/QMetaType>
+#include "encryptedfile.h"
+
class QFileInfo;
namespace Quotient {
@@ -80,8 +82,10 @@ namespace EventContent {
explicit FileInfo(const QFileInfo& fi);
explicit FileInfo(QUrl mxcUrl, qint64 payloadSize = -1,
const QMimeType& mimeType = {},
+ Omittable<EncryptedFile> file = none,
QString originalFilename = {});
FileInfo(QUrl mxcUrl, const QJsonObject& infoJson,
+ const Omittable<EncryptedFile> &file,
QString originalFilename = {});
bool isValid() const;
@@ -103,6 +107,7 @@ namespace EventContent {
QUrl url;
qint64 payloadSize;
QString originalName;
+ Omittable<EncryptedFile> file = none;
};
template <typename InfoT>
@@ -122,8 +127,10 @@ namespace EventContent {
explicit ImageInfo(const QFileInfo& fi, QSize imageSize = {});
explicit ImageInfo(const QUrl& mxcUrl, qint64 fileSize = -1,
const QMimeType& type = {}, QSize imageSize = {},
+ const Omittable<EncryptedFile> &file = none,
const QString& originalFilename = {});
ImageInfo(const QUrl& mxcUrl, const QJsonObject& infoJson,
+ const Omittable<EncryptedFile> &encryptedFile,
const QString& originalFilename = {});
void fillInfoJson(QJsonObject* infoJson) const;
@@ -142,7 +149,7 @@ namespace EventContent {
class Thumbnail : public ImageInfo {
public:
Thumbnail() = default; // Allow empty thumbnails
- Thumbnail(const QJsonObject& infoJson);
+ Thumbnail(const QJsonObject& infoJson, const Omittable<EncryptedFile> &file = none);
Thumbnail(const ImageInfo& info) : ImageInfo(info) {}
using ImageInfo::ImageInfo;
@@ -182,7 +189,7 @@ namespace EventContent {
explicit UrlBasedContent(const QJsonObject& json)
: TypedBase(json)
, InfoT(QUrl(json["url"].toString()), json["info"].toObject(),
- json["filename"].toString())
+ fromJson<Omittable<EncryptedFile>>(json["file"]), json["filename"].toString())
{
// A small hack to facilitate links creation in QML.
originalJson.insert("mediaId", InfoT::mediaId());
@@ -196,7 +203,11 @@ namespace EventContent {
void fillJson(QJsonObject* json) const override
{
Q_ASSERT(json);
- json->insert("url", InfoT::url.toString());
+ if (!InfoT::file.has_value()) {
+ json->insert("url", InfoT::url.toString());
+ } else {
+ json->insert("file", Quotient::toJson(*InfoT::file));
+ }
if (!InfoT::originalName.isEmpty())
json->insert("filename", InfoT::originalName);
json->insert("info", toInfoJson<InfoT>(*this));
diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h
index 3fa11a0f..8618ba31 100644
--- a/lib/events/roomavatarevent.h
+++ b/lib/events/roomavatarevent.h
@@ -25,7 +25,7 @@ public:
const QSize& imageSize = {},
const QString& originalFilename = {})
: RoomAvatarEvent(EventContent::ImageContent {
- mxcUrl, fileSize, mimeType, imageSize, originalFilename })
+ mxcUrl, fileSize, mimeType, imageSize, none, originalFilename })
{}
QUrl url() const { return content().url; }
diff --git a/lib/events/roomcreateevent.cpp b/lib/events/roomcreateevent.cpp
index 6558bade..ff93041c 100644
--- a/lib/events/roomcreateevent.cpp
+++ b/lib/events/roomcreateevent.cpp
@@ -5,6 +5,22 @@
using namespace Quotient;
+template <>
+struct Quotient::JsonConverter<RoomType> {
+ static RoomType load(const QJsonValue& jv)
+ {
+ const auto& roomTypeString = jv.toString();
+ for (auto it = RoomTypeStrings.begin(); it != RoomTypeStrings.end();
+ ++it)
+ if (roomTypeString == *it)
+ return RoomType(it - RoomTypeStrings.begin());
+
+ if (!roomTypeString.isEmpty())
+ qCWarning(EVENTS) << "Unknown Room Type: " << roomTypeString;
+ return RoomType::Undefined;
+ }
+};
+
bool RoomCreateEvent::isFederated() const
{
return fromJson<bool>(contentJson()["m.federate"_ls]);
@@ -26,3 +42,8 @@ bool RoomCreateEvent::isUpgrade() const
{
return contentJson().contains("predecessor"_ls);
}
+
+RoomType RoomCreateEvent::roomType() const
+{
+ return fromJson<RoomType>(contentJson()["type"_ls]);
+}
diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h
index 05e623ed..b3ad287c 100644
--- a/lib/events/roomcreateevent.h
+++ b/lib/events/roomcreateevent.h
@@ -4,6 +4,7 @@
#pragma once
#include "stateevent.h"
+#include "quotient_common.h"
namespace Quotient {
class RoomCreateEvent : public StateEventBase {
@@ -24,6 +25,7 @@ public:
QString version() const;
Predecessor predecessor() const;
bool isUpgrade() const;
+ RoomType roomType() const;
};
REGISTER_EVENT_TYPE(RoomCreateEvent)
} // namespace Quotient
diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h
index fea509c0..3174764f 100644
--- a/lib/events/roomevent.h
+++ b/lib/events/roomevent.h
@@ -14,7 +14,9 @@ class RedactionEvent;
class RoomEvent : public Event {
Q_GADGET
Q_PROPERTY(QString id READ id)
- Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT)
+ //! \deprecated Use originTimestamp instead
+ Q_PROPERTY(QDateTime timestamp READ originTimestamp CONSTANT)
+ Q_PROPERTY(QDateTime originTimestamp READ originTimestamp CONSTANT)
Q_PROPERTY(QString roomId READ roomId CONSTANT)
Q_PROPERTY(QString senderId READ senderId CONSTANT)
Q_PROPERTY(QString redactionReason READ redactionReason)
@@ -32,11 +34,12 @@ public:
QString id() const;
QDateTime originTimestamp() const;
- [[deprecated("Use originTimestamp()")]] QDateTime timestamp() const {
- return originTimestamp();
- }
QString roomId() const;
QString senderId() const;
+ //! \brief Determine whether the event has been replaced
+ //!
+ //! \return true if this event has been overridden by another event
+ //! with `"rel_type": "m.replace"`; false otherwise
bool isReplaced() const;
QString replacedBy() const;
bool isRedacted() const { return bool(_redactedBecause); }
diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp
index 71f85363..9b46594e 100644
--- a/lib/events/roommessageevent.cpp
+++ b/lib/events/roommessageevent.cpp
@@ -145,21 +145,21 @@ TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile)
auto mimeTypeName = mimeType.name();
if (mimeTypeName.startsWith("image/"))
return new ImageContent(localUrl, file.size(), mimeType,
- QImageReader(filePath).size(),
+ QImageReader(filePath).size(), none,
file.fileName());
// duration can only be obtained asynchronously and can only be reliably
// done by starting to play the file. Left for a future implementation.
if (mimeTypeName.startsWith("video/"))
return new VideoContent(localUrl, file.size(), mimeType,
- QMediaResource(localUrl).resolution(),
+ QMediaResource(localUrl).resolution(), none,
file.fileName());
if (mimeTypeName.startsWith("audio/"))
- return new AudioContent(localUrl, file.size(), mimeType,
+ return new AudioContent(localUrl, file.size(), mimeType, none,
file.fileName());
}
- return new FileContent(localUrl, file.size(), mimeType, file.fileName());
+ return new FileContent(localUrl, file.size(), mimeType, none, file.fileName());
}
RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h
index 7bcda2ba..88d3b74c 100644
--- a/lib/events/roommessageevent.h
+++ b/lib/events/roommessageevent.h
@@ -62,9 +62,26 @@ public:
_content.data());
}
QMimeType mimeType() const;
+ //! \brief Determine whether the message has text content
+ //!
+ //! \return true, if the message type is one of m.text, m.notice, m.emote,
+ //! or the message type is unspecified (in which case plainBody()
+ //! can still be examined); false otherwise
bool hasTextContent() const;
+ //! \brief Determine whether the message has a file/attachment
+ //!
+ //! \return true, if the message has a data structure corresponding to
+ //! a file (such as m.file or m.audio); false otherwise
bool hasFileContent() const;
+ //! \brief Determine whether the message has a thumbnail
+ //!
+ //! \return true, if the message has a data structure corresponding to
+ //! a thumbnail (the message type may be one for visual content,
+ //! such as m.image, or generic binary content, i.e. m.file);
+ //! false otherwise
bool hasThumbnail() const;
+ //! \brief Obtain id of an event replaced by the current one
+ //! \sa RoomEvent::isReplaced, RoomEvent::replacedBy
QString replacedEvent() const;
static QString rawMsgTypeForUrl(const QUrl& url);
diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h
index 1415f709..bc414a5f 100644
--- a/lib/events/stateevent.h
+++ b/lib/events/stateevent.h
@@ -100,10 +100,6 @@ public:
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;