Page MenuHomePhorge

No OneTemporary

Size
12 KB
Referenced Files
None
Subscribers
None
diff --git a/src/client/matrix-uri.cpp b/src/client/matrix-uri.cpp
index c52d7fe..fbfb0f0 100644
--- a/src/client/matrix-uri.cpp
+++ b/src/client/matrix-uri.cpp
@@ -1,345 +1,345 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2022 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <boost/algorithm/string/split.hpp>
#include <immer/map.hpp>
#include "matrix-uri.hpp"
namespace Kazv
{
std::string desigilify(std::string id)
{
if (!id.empty() && (
- id[0] == '#' || id[0] == '!' || id[0] == '@'
+ id[0] == '#' || id[0] == '!' || id[0] == '@' || id[0] == '$'
)) {
id.erase(id.begin());
}
return id;
}
static const std::string matrixProtocol = "matrix";
struct MatrixUri::Private
{
enum Type
{
Invalid = 0x0,
RoomId = 0x1,
RoomAlias = 0x2,
User = 0x4,
Event = 0x8,
};
unsigned int type{Invalid};
std::string upperLevel{};
std::string lowerLevel{};
std::string action{};
immer::flex_vector<std::string> routing{};
immer::map<std::string, std::string> query{};
std::string fragment{};
};
MatrixUri::MatrixUri()
: m_d(std::make_unique<Private>())
{
}
MatrixUri::MatrixUri(std::unique_ptr<Private> d)
: m_d(std::move(d))
{
}
MatrixUri MatrixUri::withUser(std::string userId)
{
return MatrixUri(std::unique_ptr<Private>(new Private{Private::User, std::move(userId)}));
}
MatrixUri MatrixUri::withRoomById(std::string roomId)
{
return MatrixUri(std::unique_ptr<Private>(new Private{Private::RoomId, std::move(roomId)}));
}
MatrixUri MatrixUri::withRoomByAlias(std::string alias)
{
return MatrixUri(std::unique_ptr<Private>(new Private{Private::RoomAlias, std::move(alias)}));
}
MatrixUri::~MatrixUri() = default;
KAZV_DEFINE_COPYABLE_UNIQUE_PTR(MatrixUri, m_d);
bool MatrixUri::isValid() const
{
return m_d->type;
}
bool MatrixUri::isRoom() const
{
return m_d->type == Private::RoomId || m_d->type == Private::RoomAlias;
}
bool MatrixUri::isUser() const
{
return m_d->type == Private::User;
}
bool MatrixUri::isEvent() const
{
return m_d->type & Private::Event;
}
std::string MatrixUri::id() const
{
if (isEvent()) {
return m_d->lowerLevel;
} else {
return m_d->upperLevel;
}
}
std::string MatrixUri::userOrRoom() const
{
return m_d->upperLevel;
}
std::string MatrixUri::action() const
{
return m_d->action;
}
MatrixUri MatrixUri::withAction(std::string act) const
{
auto r = *this;
r.m_d->action = act;
return r;
}
auto MatrixUri::routing() const -> immer::flex_vector<std::string>
{
return m_d->routing;
}
MatrixUri MatrixUri::withVia(std::string authority) const
{
auto r = *this;
r.m_d->routing = std::move(r.m_d->routing).push_back(authority);
return r;
}
std::string MatrixUri::query(std::string key) const
{
if (key == "via") {
return m_d->routing.size() ? m_d->routing.back() : std::string();
} else if (key == "action") {
return m_d->action;
} else {
return m_d->query[key];
}
}
MatrixUri MatrixUri::withQuery(std::string key, std::string value) const
{
if (key == "via") {
return withVia(std::move(value));
} else if (key == "action") {
return withAction(std::move(value));
} else {
auto r = *this;
r.m_d->query = std::move(r.m_d->query).set(std::move(key), std::move(value));
return r;
}
}
std::string MatrixUri::fragment() const
{
return m_d->fragment;
}
MatrixUri MatrixUri::withFragment(std::string frag) const
{
auto r = *this;
r.m_d->fragment = frag;
return r;
}
std::string MatrixUri::toString() const
{
std::string res = matrixProtocol + ":";
bool hasQuery = false;
auto putUpperLevel = [this, &res]() {
if (m_d->type & Private::RoomId) {
res += "roomid/";
} else if (m_d->type & Private::RoomAlias) {
res += "r/";
} else if (m_d->type & Private::User) {
res += "u/";
}
res += desigilify(m_d->upperLevel);
};
auto putLowerLevel = [this, &res]() {
if (m_d->type & Private::Event) {
res += "/e/" + desigilify(m_d->lowerLevel);
}
};
auto putQueryPrefix = [this, &res, &hasQuery]() {
if (hasQuery) {
res += "&";
} else {
hasQuery = true;
res += "?";
}
};
auto putAction = [this, &res, putQueryPrefix]() {
if (!m_d->action.empty()) {
putQueryPrefix();
res += "action=" + m_d->action;
}
};
auto putRouting = [this, &res, putQueryPrefix]() {
for (const auto &value: m_d->routing) {
putQueryPrefix();
res += "via=" + value;
}
};
auto putCustomQuery = [this, &res, putQueryPrefix]() {
for (const auto &[key, value]: m_d->query) {
putQueryPrefix();
res += key + "=" + value;
}
};
auto putFragment = [this, &res]() {
if (!m_d->fragment.empty()) {
res += "#" + m_d->fragment;
}
};
putUpperLevel();
putLowerLevel();
putAction();
putRouting();
putCustomQuery();
putFragment();
return res;
}
MatrixUri MatrixUri::parse(std::string uri)
{
// Ref: https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/2312-matrix-uri.md#recommended-implementation
MatrixUri res;
bool valid = true;
std::vector<std::string> entities;
std::string query;
std::string fragment;
auto verifyProto = [&res, &valid, &uri]() {
const auto &protoPart = matrixProtocol + ":";
if (uri.substr(0, protoPart.size()) != protoPart) {
valid = false;
}
uri.erase(uri.begin(), uri.begin() + protoPart.size());
};
auto withValid = [valid](auto func) {
if (valid) {
func();
}
};
auto splitParts = [&res, &valid, &uri, &entities, &query, &fragment]() {
auto fragPos = uri.find('#');
if (fragPos != std::string::npos) {
fragment = std::string(uri.begin() + fragPos + 1, uri.end());
uri.erase(uri.begin() + fragPos, uri.end());
}
auto queryPos = uri.find('?');
if (queryPos != std::string::npos) {
query = std::string(uri.begin() + queryPos + 1, uri.end());
uri.erase(uri.begin() + queryPos, uri.end());
}
boost::algorithm::split(entities, uri, [](char c) { return c == '/'; });
if (entities.size() != 2 && entities.size() != 4) {
valid = false;
}
};
auto putUpperLevel = [&entities, &res, &valid]() {
std::string sigil;
if (entities[0] == "room" || entities[0] == "r") {
res.m_d->type |= Private::RoomAlias;
sigil = "#";
} else if (entities[0] == "user" || entities[0] == "u") {
res.m_d->type |= Private::User;
sigil = "@";
} else if (entities[0] == "roomid") {
res.m_d->type |= Private::RoomId;
sigil = "!";
} else {
valid = false;
}
res.m_d->upperLevel = sigil + entities[1];
entities.erase(entities.begin(), entities.begin() + 2);
};
auto putLowerLevel = [&entities, &res, &valid]() {
if (entities.size() < 2) {
return;
}
if (entities[0] == "event" || entities[0] == "e") {
res.m_d->type |= Private::Event;
- res.m_d->lowerLevel = "!" + entities[1];
+ res.m_d->lowerLevel = "$" + entities[1];
} else {
valid = false;
}
};
auto putQueryParams = [&query, &res]() {
std::vector<std::string> queryParams;
boost::algorithm::split(queryParams, query, [](char c) { return c == '&'; });
for (const auto &entry : queryParams) {
auto valPos = entry.find('=');
auto key = entry.substr(0, valPos);
auto value = entry.substr(valPos + 1);
res = res.withQuery(key, value);
}
};
auto putFragment = [&fragment, &res]() {
if (!fragment.empty()) {
res = res.withFragment(fragment);
}
};
verifyProto();
withValid(splitParts);
withValid(putUpperLevel);
withValid(putLowerLevel);
withValid(putQueryParams);
withValid(putFragment);
if (valid) {
return res;
} else {
return MatrixUri();
}
}
}
diff --git a/src/tests/client/matrix-uri-test.cpp b/src/tests/client/matrix-uri-test.cpp
index d09b167..8472215 100644
--- a/src/tests/client/matrix-uri-test.cpp
+++ b/src/tests/client/matrix-uri-test.cpp
@@ -1,62 +1,86 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2022 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <catch2/catch.hpp>
#include <matrix-uri.hpp>
using namespace Kazv;
TEST_CASE("MatrixUri::parse()", "[client][matrix-uri]")
{
SECTION("room by alias")
{
auto uri = MatrixUri::parse("matrix:r/somewhere:example.org");
REQUIRE(uri.isValid());
REQUIRE(uri.isRoom());
REQUIRE(uri.id() == "#somewhere:example.org");
}
SECTION("room by id")
{
auto uri = MatrixUri::parse("matrix:roomid/somewhere:example.org?via=elsewhere.ca");
REQUIRE(uri.isValid());
REQUIRE(uri.isRoom());
REQUIRE(uri.id() == "!somewhere:example.org");
REQUIRE(uri.routing() == immer::flex_vector<std::string>{"elsewhere.ca"});
}
SECTION("room by alias with event")
{
auto uri = MatrixUri::parse("matrix:r/somewhere:example.org/e/event");
REQUIRE(uri.isValid());
REQUIRE(uri.isEvent());
REQUIRE(uri.userOrRoom() == "#somewhere:example.org");
- REQUIRE(uri.id() == "!event");
+ REQUIRE(uri.id() == "$event");
}
SECTION("room by id with event")
{
auto uri = MatrixUri::parse("matrix:roomid/somewhere:example.org/e/event?via=elsewhere.ca");
REQUIRE(uri.isValid());
REQUIRE(uri.isEvent());
REQUIRE(uri.userOrRoom() == "!somewhere:example.org");
- REQUIRE(uri.id() == "!event");
+ REQUIRE(uri.id() == "$event");
REQUIRE(uri.routing() == immer::flex_vector<std::string>{"elsewhere.ca"});
}
SECTION("user")
{
auto uri = MatrixUri::parse("matrix:u/alice:example.org?action=chat");
REQUIRE(uri.isValid());
REQUIRE(uri.isUser());
REQUIRE(uri.userOrRoom() == "@alice:example.org");
REQUIRE(uri.id() == "@alice:example.org");
REQUIRE(uri.action() == "chat");
}
}
+
+TEST_CASE("desigilify()", "[client][matrix-uri]")
+{
+ SECTION("It removes #, !, @ and $")
+ {
+ REQUIRE(desigilify("#a") == "a");
+ REQUIRE(desigilify("!a") == "a");
+ REQUIRE(desigilify("@a") == "a");
+ REQUIRE(desigilify("$a") == "a");
+ }
+
+ SECTION("It does not remove other symbols")
+ {
+ REQUIRE(desigilify("*a") == "*a");
+ REQUIRE(desigilify("ua") == "ua");
+ REQUIRE(desigilify("喵喵") == "喵喵");
+ }
+
+ SECTION("It does not crash with empty identifiers")
+ {
+ REQUIRE(desigilify("@") == "");
+ REQUIRE(desigilify("") == "");
+ }
+}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Oct 10, 3:39 PM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1785305
Default Alt Text
(12 KB)

Event Timeline