Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85804827
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
12 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Oct 10, 3:39 PM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1785305
Default Alt Text
(12 KB)
Attached To
Mode
rL libkazv
Attached
Detach File
Event Timeline
Log In to Comment