Page MenuHomePhorge

No OneTemporary

Size
16 KB
Referenced Files
None
Subscribers
None
diff --git a/src/base/event.cpp b/src/base/event.cpp
index 4695c4c..db6211e 100644
--- a/src/base/event.cpp
+++ b/src/base/event.cpp
@@ -1,173 +1,187 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include "libkazv-config.hpp"
#include <optional>
#include "event.hpp"
#include "json-utils.hpp"
namespace Kazv
{
const JsonWrap Event::notYetDecryptedEvent = {
json{{"type", "m.room.message"},
{"content", {
{"msgtype", "xyz.tusooa.kazv.not.yet.decrypted"},
{"body", "**This message has not yet been decrypted.**"}}}}};
Event::Event() : m_json(json::object()) {}
Event::Event(JsonWrap j)
: m_json(j) {
using namespace std::string_literals;
if (m_json.get().contains("type"s)) {
auto type = m_json.get().at("type"s).get<std::string>();
if (type == "m.room.encrypted"s) {
m_encrypted = true;
m_decryptedJson = notYetDecryptedEvent;
}
}
}
Event Event::fromSync(Event e, std::string roomId) {
auto j = e.originalJson().get();
j["room_id"] = roomId;
return Event(j);
}
std::string Event::id() const {
// the decrypted json does not have an event id
return originalJson().get().contains("event_id")
? originalJson().get().at("event_id")
: "";
}
std::string Event::sender() const {
return originalJson().get().contains("sender")
? originalJson().get().at("sender")
: "";
}
Timestamp Event::originServerTs() const {
return (originalJson().get().contains("origin_server_ts")
&& originalJson().get().at("origin_server_ts").is_number())
? originalJson().get().at("origin_server_ts").template get<Timestamp>()
: 0;
}
std::string Event::type() const {
return raw().get().contains("type")
? raw().get().at("type")
: "";
}
JsonWrap Event::content() const {
return raw().get().contains("content")
? raw().get().at("content")
: json::object();
}
std::string Event::stateKey() const {
return raw().get().contains("state_key")
? raw().get().at("state_key")
: "";
}
bool Event::isState() const
{
return raw().get().contains("state_key");
}
/// returns the decrypted json
JsonWrap Event::raw() const {
return m_encrypted ? decryptedJson() : m_json;
}
/// returns the original json we fetched, probably encrypted.
JsonWrap Event::originalJson() const {
return m_json;
}
JsonWrap Event::decryptedJson() const {
return m_decryptedJson;
}
bool Event::encrypted() const {
return m_encrypted;
}
bool Event::decrypted() const {
return m_decrypted == Decrypted;
}
Event Event::setDecryptedJson(JsonWrap decryptedJson, DecryptionStatus decrypted) const {
Event e(*this);
e.m_decryptedJson = decryptedJson;
e.m_decrypted = decrypted;
return e;
}
bool Event::redacted() const
{
auto ptr = nlohmann::json_pointer<std::string>("/unsigned/redacted_because");
return originalJson().get().contains(ptr)
&& originalJson().get()[ptr].is_object();
}
std::string Event::replyingTo() const
{
auto [relType, eventId] = relationship();
if (relType == "m.in_reply_to") {
return eventId;
}
return "";
}
static std::optional<std::pair<std::string, std::string>> getRel(const json &eventJson)
{
if (!hasAtThat(eventJson, "content", &json::is_object)) {
return std::nullopt;
}
auto content = eventJson["content"];
if (hasAtThat(content, "m.relates_to", &json::is_object)) {
if (hasAtThat(content["m.relates_to"], "event_id", &json::is_string)
&& hasAtThat(content["m.relates_to"], "rel_type", &json::is_string)) {
return std::make_pair(
content["m.relates_to"]["rel_type"].template get<std::string>(),
content["m.relates_to"]["event_id"].template get<std::string>()
);
} else if (hasAtThat(content["m.relates_to"], "m.in_reply_to", &json::is_object)
&& hasAtThat(content["m.relates_to"]["m.in_reply_to"], "event_id", &json::is_string)) {
return std::make_pair(
std::string("m.in_reply_to"),
content["m.relates_to"]["m.in_reply_to"]["event_id"].template get<std::string>()
);
}
}
return std::nullopt;
}
std::pair<std::string/* relType */, std::string/* eventId */> Event::relationship() const
{
std::optional<std::pair<std::string/* relType */, std::string/* eventId */>> relOpt;
if ((relOpt = getRel(originalJson().get())).has_value()) {
return relOpt.value();
} else if (encrypted() && (relOpt = getRel(raw().get())).has_value()) {
return relOpt.value();
}
return {"", ""};
}
+ JsonWrap Event::mRelatesTo() const
+ {
+ if (hasAtThat(originalJson().get(),
+ "/content/m.relates_to"_json_pointer,
+ &json::is_object)) {
+ return originalJson().get()["/content/m.relates_to"_json_pointer];
+ } else if (encrypted() && hasAtThat(raw().get(),
+ "/content/m.relates_to"_json_pointer,
+ &json::is_object)) {
+ return raw().get()["/content/m.relates_to"_json_pointer];
+ }
+ return json::object();
+ }
+
bool operator==(const Event &a, const Event &b)
{
return a.id() == b.id()
&& a.originalJson() == b.originalJson()
&& a.raw() == b.raw();
}
}
diff --git a/src/base/event.hpp b/src/base/event.hpp
index 72cc4d0..e8ce5b9 100644
--- a/src/base/event.hpp
+++ b/src/base/event.hpp
@@ -1,121 +1,128 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include "libkazv-config.hpp"
#include <string>
#include <cstdint>
#include "jsonwrap.hpp"
namespace Kazv
{
using Timestamp = std::int_fast64_t;
class Event
{
public:
static const JsonWrap notYetDecryptedEvent;
enum DecryptionStatus {
NotDecrypted,
Decrypted
};
Event();
Event(JsonWrap j);
static Event fromSync(Event e, std::string roomId);
/// returns the id of this event
std::string id() const;
std::string sender() const;
Timestamp originServerTs() const;
std::string type() const;
std::string stateKey() const;
/**
* @return whether this event is a state event.
* An event is considered a state event if and only if
* it has a maybe empty stateKey.
*/
bool isState() const;
JsonWrap content() const;
/// returns the decrypted json
JsonWrap raw() const;
/// returns the original json we fetched, probably encrypted.
JsonWrap originalJson() const;
JsonWrap decryptedJson() const;
bool encrypted() const;
bool decrypted() const;
/// internal. only to be called from inside the client.
Event setDecryptedJson(JsonWrap decryptedJson, DecryptionStatus decrypted) const;
/// returns whether this event has been redacted.
bool redacted() const;
/**
* Get the event id this event is replying to.
*
* @return The event id this event is replying to, or empty string if this
* event is not a reply.
*/
std::string replyingTo() const;
/**
* Get the relationship that this event contains.
*
* @return A Pair containing the rel_type and event_id in the m.relates_to section
* of this event, or a Pair of empty strings if there are not any.
*/
std::pair<std::string/* relType */, std::string/* eventId */> relationship() const;
+ /**
+ * Get the m.relates_to object in the event.
+ *
+ * @return The m.relates_to object of this event.
+ */
+ JsonWrap mRelatesTo() const;
+
template<class Archive>
void serialize(Archive &ar, std::uint32_t const /*version*/ ) {
ar & m_json & m_decryptedJson & m_decrypted & m_encrypted;
}
private:
JsonWrap m_json;
JsonWrap m_decryptedJson;
DecryptionStatus m_decrypted{NotDecrypted};
bool m_encrypted{false};
};
bool operator==(const Event &a, const Event &b);
inline bool operator!=(const Event &a, const Event &b) { return !(a == b); };
}
BOOST_CLASS_VERSION(Kazv::Event, 0)
namespace nlohmann
{
template <>
struct adl_serializer<Kazv::Event> {
static void to_json(json& j, Kazv::Event w) {
j = w.originalJson();
}
static void from_json(const json& j, Kazv::Event &w) {
w = Kazv::Event(Kazv::JsonWrap(j));
}
};
}
diff --git a/src/tests/event-test.cpp b/src/tests/event-test.cpp
index 1eb9ce8..2a2f99d 100644
--- a/src/tests/event-test.cpp
+++ b/src/tests/event-test.cpp
@@ -1,162 +1,172 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <catch2/catch_all.hpp>
#include <event.hpp>
using namespace Kazv;
TEST_CASE("Should distinguish between state events and other events", "[base][event]")
{
Event state = R"({
"state_key": "",
"sender": "@example:example.org",
"type": "moe.kazv.mxc.custom.state.type",
"event_id": "!dummy",
"origin_server_ts": 1234,
"content": {}
})"_json;
REQUIRE(state.isState());
Event nonState = R"({
"sender": "@example:example.org",
"type": "moe.kazv.mxc.custom.non.state.type",
"event_id": "!dummy",
"origin_server_ts": 1234,
"content": {}
})"_json;
REQUIRE(!nonState.isState());
}
TEST_CASE("redacted()", "[base][event]")
{
auto eventJson = R"({
"state_key": "",
"sender": "@example:example.org",
"type": "moe.kazv.mxc.custom.state.type",
"event_id": "!dummy",
"origin_server_ts": 1234,
"content": {}
})"_json;
REQUIRE(!Event(eventJson).redacted());
eventJson["unsigned"] = json::object({{"redacted_because", json::object()}});
REQUIRE(Event(eventJson).redacted());
}
TEST_CASE("relationship()", "[base][event]")
{
auto eventJson = json{
{"sender", "@example:example.org"},
{"type", "moe.kazv.mxc.custom.state.type"},
{"event_id", "$dummy"},
{"origin_server_ts", 1234},
{"content", {
{"m.relates_to", {
{"rel_type", "moe.kazv.mxc.some-type"},
{"event_id", "$another"},
},
}}},
};
WHEN("m.relates_to is a normal relation") {
REQUIRE(Event(eventJson).relationship() == std::pair<std::string, std::string>{"moe.kazv.mxc.some-type", "$another"});
REQUIRE(Event(eventJson).replyingTo() == "");
+ REQUIRE(Event(eventJson).mRelatesTo().get() == eventJson["content"]["m.relates_to"]);
}
WHEN("m.relates_to is a reply") {
eventJson["content"]["m.relates_to"] = {
{"m.in_reply_to", {
{"event_id", "$another"}
}},
};
REQUIRE(Event(eventJson).relationship() == std::pair<std::string, std::string>{"m.in_reply_to", "$another"});
REQUIRE(Event(eventJson).replyingTo() == "$another");
+ REQUIRE(Event(eventJson).mRelatesTo().get() == eventJson["content"]["m.relates_to"]);
}
WHEN("m.relates_to is missing rel_type") {
eventJson["content"]["m.relates_to"].erase("rel_type");
REQUIRE(Event(eventJson).relationship() == std::pair<std::string, std::string>{"", ""});
+ REQUIRE(Event(eventJson).mRelatesTo().get() == eventJson["content"]["m.relates_to"]);
}
WHEN("m.relates_to is missing event_id") {
eventJson["content"]["m.relates_to"].erase("event_id");
REQUIRE(Event(eventJson).relationship() == std::pair<std::string, std::string>{"", ""});
+ REQUIRE(Event(eventJson).mRelatesTo().get() == eventJson["content"]["m.relates_to"]);
}
WHEN("m.relates_to is not an object") {
eventJson["content"]["m.relates_to"] = json::array();
REQUIRE(Event(eventJson).relationship() == std::pair<std::string, std::string>{"", ""});
+ REQUIRE(Event(eventJson).mRelatesTo().get() == json::object());
}
WHEN("m.relates_to does not exist") {
eventJson["content"].erase("m.relates_to");
REQUIRE(Event(eventJson).relationship() == std::pair<std::string, std::string>{"", ""});
+ REQUIRE(Event(eventJson).mRelatesTo().get() == json::object());
}
}
TEST_CASE("relationship(), with encrypted event", "[base][event]")
{
auto eventJsonWithRel = json{
{"sender", "@example:example.org"},
{"type", "moe.kazv.mxc.custom.state.type"},
{"event_id", "$dummy"},
{"origin_server_ts", 1234},
{"content", {
{"m.relates_to", {
{"rel_type", "moe.kazv.mxc.some-type"},
{"event_id", "$another"},
},
}}},
};
auto eventJson = eventJsonWithRel;
eventJson["content"].erase("m.relates_to");
auto plainJsonWithRel = eventJsonWithRel;
plainJsonWithRel["type"] = "m.room.encrypted";
plainJsonWithRel["content"]["m.relates_to"] = {
{"rel_type", "moe.kazv.mxc.some-other-type"},
{"event_id", "$another-2"},
};
auto plainJson = plainJsonWithRel;
plainJson["content"].erase("m.relates_to");
WHEN("plaintext with relationship / ciphertext with relationship") {
auto event = Event(plainJsonWithRel).setDecryptedJson(eventJsonWithRel, Event::Decrypted);
THEN("should use the relationship within plaintext") {
REQUIRE(event.relationship() == std::pair<std::string, std::string>{"moe.kazv.mxc.some-other-type", "$another-2"});
+ REQUIRE(event.mRelatesTo().get() == plainJsonWithRel["content"]["m.relates_to"]);
}
}
WHEN("plaintext without relationship / ciphertext with relationship") {
auto event = Event(plainJson).setDecryptedJson(eventJsonWithRel, Event::Decrypted);
THEN("should use the relationship within ciphertext") {
REQUIRE(event.relationship() == std::pair<std::string, std::string>{"moe.kazv.mxc.some-type", "$another"});
+ REQUIRE(event.mRelatesTo().get() == eventJsonWithRel["content"]["m.relates_to"]);
}
}
WHEN("plaintext with relationship / ciphertext without relationship") {
auto event = Event(plainJsonWithRel).setDecryptedJson(eventJson, Event::Decrypted);
THEN("should use the relationship within plaintext") {
REQUIRE(event.relationship() == std::pair<std::string, std::string>{"moe.kazv.mxc.some-other-type", "$another-2"});
+ REQUIRE(event.mRelatesTo().get() == plainJsonWithRel["content"]["m.relates_to"]);
}
}
WHEN("plaintext without relationship / ciphertext without relationship") {
auto event = Event(plainJson).setDecryptedJson(eventJson, Event::Decrypted);
THEN("should return empty pair") {
REQUIRE(event.relationship() == std::pair<std::string, std::string>{"", ""});
+ REQUIRE(event.mRelatesTo().get() == json::object());
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Aug 9, 4:23 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1724196
Default Alt Text
(16 KB)

Event Timeline