Features: iq::Bind and iq::Roster #4
7 changed files with 136 additions and 25 deletions
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
|
@ -12,6 +12,18 @@
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"preLaunchTask": "GCC: Build"
|
"preLaunchTask": "GCC: Build"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug: tests",
|
||||||
|
"program": "${workspaceFolder}/build/larra_xmpp_tests",
|
||||||
|
"args": [
|
||||||
|
// --gtest_filter=POSTIVE_PATTERNS[-NEGATIVE_PATTERNS]
|
||||||
|
// "--gtest_filter=Roster*"
|
||||||
|
],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"preLaunchTask": "GCC: Build"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
|
@ -104,7 +104,7 @@
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"clear": true
|
"clear": true
|
||||||
},
|
},
|
||||||
"hide": true,
|
"hide": false,
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build"
|
"kind": "build"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <spdlog/common.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <boost/asio/co_spawn.hpp>
|
#include <boost/asio/co_spawn.hpp>
|
||||||
|
@ -7,6 +8,8 @@
|
||||||
#include <larra/printer_stream.hpp>
|
#include <larra/printer_stream.hpp>
|
||||||
#include <print>
|
#include <print>
|
||||||
|
|
||||||
|
namespace iq = larra::xmpp::iq;
|
||||||
|
|
||||||
auto Coroutine() -> boost::asio::awaitable<void> {
|
auto Coroutine() -> boost::asio::awaitable<void> {
|
||||||
SPDLOG_INFO("Connecting client...");
|
SPDLOG_INFO("Connecting client...");
|
||||||
|
|
||||||
|
@ -14,11 +17,26 @@ auto Coroutine() -> boost::asio::awaitable<void> {
|
||||||
auto client = co_await larra::xmpp::client::CreateClient<larra::xmpp::PrintStream<boost::asio::ip::tcp::socket>>(
|
auto client = co_await larra::xmpp::client::CreateClient<larra::xmpp::PrintStream<boost::asio::ip::tcp::socket>>(
|
||||||
larra::xmpp::PlainUserAccount{.jid = {.username = "test1", .server = "localhost"}, .password = "test1"},
|
larra::xmpp::PlainUserAccount{.jid = {.username = "test1", .server = "localhost"}, .password = "test1"},
|
||||||
{.useTls = larra::xmpp::client::Options::kNever});
|
{.useTls = larra::xmpp::client::Options::kNever});
|
||||||
|
|
||||||
|
// rfc6120 7.1
|
||||||
|
// After a client authenticates with a server,
|
||||||
|
// it MUST bind a specific resource to the stream so that the server can properly address the client.
|
||||||
co_await std::visit(
|
co_await std::visit(
|
||||||
[](auto& client) -> boost::asio::awaitable<void> {
|
[](auto& client) -> boost::asio::awaitable<void> {
|
||||||
|
co_await client.CreateResourceBind();
|
||||||
|
},
|
||||||
|
client);
|
||||||
|
|
||||||
|
// rfc6120 2.2
|
||||||
|
// Upon authenticating with a server and binding a resource (thus becoming a connected resource as defined in [XMPP‑CORE]),
|
||||||
|
// a client SHOULD request the roster before sending initial presence
|
||||||
|
co_await std::visit(
|
||||||
|
[](auto& client) -> boost::asio::awaitable<void> {
|
||||||
|
SPDLOG_INFO("Send presence: Available");
|
||||||
co_await client.Send(larra::xmpp::presence::c2s::Available{});
|
co_await client.Send(larra::xmpp::presence::c2s::Available{});
|
||||||
},
|
},
|
||||||
client);
|
client);
|
||||||
|
|
||||||
} catch(const std::exception& err) {
|
} catch(const std::exception& err) {
|
||||||
SPDLOG_ERROR("{}", err.what());
|
SPDLOG_ERROR("{}", err.what());
|
||||||
co_return;
|
co_return;
|
||||||
|
|
24
library/include/larra/bind.hpp
Normal file
24
library/include/larra/bind.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
#include <libxml++/libxml++.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <larra/iq.hpp>
|
||||||
|
#include <larra/jid.hpp>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace larra::xmpp::iq {
|
||||||
|
|
||||||
|
struct Bind {
|
||||||
|
static constexpr auto kDefaultName = "bind";
|
||||||
|
static constexpr auto kDefaultNamespace = "urn:ietf:params:xml:ns:xmpp-bind";
|
||||||
|
|
||||||
|
std::optional<FullJid> jid;
|
||||||
|
|
||||||
|
friend auto operator<<(xmlpp::Element* element, const Bind& bind) -> void;
|
||||||
|
[[nodiscard]] static auto Parse(xmlpp::Element* element) -> Bind;
|
||||||
|
};
|
||||||
|
|
||||||
|
using SetBind = Set<Bind>;
|
||||||
|
using ResultBind = Result<Bind>;
|
||||||
|
|
||||||
|
} // namespace larra::xmpp::iq
|
|
@ -11,6 +11,7 @@
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
#include <boost/asio/use_awaitable.hpp>
|
#include <boost/asio/use_awaitable.hpp>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
#include <larra/bind.hpp>
|
||||||
#include <larra/client/challenge_response.hpp>
|
#include <larra/client/challenge_response.hpp>
|
||||||
#include <larra/client/options.hpp>
|
#include <larra/client/options.hpp>
|
||||||
#include <larra/client/starttls_response.hpp>
|
#include <larra/client/starttls_response.hpp>
|
||||||
|
@ -21,6 +22,12 @@
|
||||||
#include <larra/user_account.hpp>
|
#include <larra/user_account.hpp>
|
||||||
#include <larra/xml_stream.hpp>
|
#include <larra/xml_stream.hpp>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace rng = std::ranges;
|
||||||
|
namespace views = std::views;
|
||||||
|
namespace iq = larra::xmpp::iq;
|
||||||
|
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
|
||||||
constexpr auto kDefaultXmppPort = 5222;
|
constexpr auto kDefaultXmppPort = 5222;
|
||||||
|
@ -29,12 +36,10 @@ constexpr auto kDefaultXmppPort = 5222;
|
||||||
|
|
||||||
namespace larra::xmpp::client {
|
namespace larra::xmpp::client {
|
||||||
|
|
||||||
namespace rng = std::ranges;
|
|
||||||
namespace views = std::views;
|
|
||||||
|
|
||||||
template <typename Connection>
|
template <typename Connection>
|
||||||
struct Client {
|
struct Client {
|
||||||
constexpr Client(BareJid jid, XmlStream<Connection> connection) : jid(std::move(jid)), connection(std::move(connection)) {};
|
constexpr Client(BareJid jid, XmlStream<Connection> connection) : jid(std::move(jid)), connection(std::move(connection)) {
|
||||||
|
}
|
||||||
template <boost::asio::completion_token_for<void()> Token = boost::asio::use_awaitable_t<>>
|
template <boost::asio::completion_token_for<void()> Token = boost::asio::use_awaitable_t<>>
|
||||||
constexpr auto Close(Token token = {}) {
|
constexpr auto Close(Token token = {}) {
|
||||||
this->active = false;
|
this->active = false;
|
||||||
|
@ -68,14 +73,31 @@ struct Client {
|
||||||
auto Send(const T& object) -> boost::asio::awaitable<void> {
|
auto Send(const T& object) -> boost::asio::awaitable<void> {
|
||||||
co_await this->connection.Send(object);
|
co_await this->connection.Send(object);
|
||||||
}
|
}
|
||||||
[[nodiscard]] constexpr auto Jid() const -> const BareJid& {
|
[[nodiscard]] constexpr auto Jid() const -> const FullJid& {
|
||||||
return this->jid;
|
return this->jid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto CreateResourceBind() -> boost::asio::awaitable<void> {
|
||||||
|
SPDLOG_INFO("Send IQ: Set::Bind");
|
||||||
|
co_await this->Send(::iq::SetBind{.id = "1", .payload = {}});
|
||||||
|
|
||||||
|
auto set_bind_response = co_await connection.template Read<Iq<::iq::Bind>>();
|
||||||
|
std::visit(utempl::Overloaded(
|
||||||
|
[](auto error) {
|
||||||
|
throw "Error response on IQ: Set::Bind: ''"; // TODO(unknown): Add exact error parsing
|
||||||
|
},
|
||||||
|
[&](::iq::ResultBind r) {
|
||||||
|
jid.resource = std::move(r.payload.jid->resource);
|
||||||
|
SPDLOG_INFO("Allocated resource: {}", jid.resource);
|
||||||
|
}),
|
||||||
|
set_bind_response);
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool active = true;
|
bool active = true;
|
||||||
|
|||||||
XmlStream<Connection> connection{};
|
XmlStream<Connection> connection{};
|
||||||
BareJid jid;
|
FullJid jid;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StartTlsNegotiationError : std::runtime_error {
|
struct StartTlsNegotiationError : std::runtime_error {
|
||||||
|
|
|
@ -7,24 +7,6 @@
|
||||||
|
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
|
||||||
struct BareJid {
|
|
||||||
std::string username;
|
|
||||||
std::string server;
|
|
||||||
[[nodiscard]] static auto Parse(std::string_view jid) -> BareJid;
|
|
||||||
friend auto ToString(const BareJid& jid) -> std::string;
|
|
||||||
|
|
||||||
constexpr auto operator==(const BareJid&) const -> bool = default;
|
|
||||||
template <typename Self>
|
|
||||||
[[nodiscard]] constexpr auto Username(this Self&& self, std::string username) -> std::decay_t<Self> {
|
|
||||||
return utils::FieldSetHelper::With<"username", BareJid>(std::forward<Self>(self), std::move(username));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Self>
|
|
||||||
[[nodiscard]] constexpr auto Server(this Self&& self, std::string server) -> std::decay_t<Self> {
|
|
||||||
return utils::FieldSetHelper::With<"server", BareJid>(std::forward<Self>(self), std::move(server));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BareResourceJid {
|
struct BareResourceJid {
|
||||||
std::string server;
|
std::string server;
|
||||||
std::string resource;
|
std::string resource;
|
||||||
|
@ -69,6 +51,28 @@ struct FullJid {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BareJid {
|
||||||
|
std::string username;
|
||||||
|
std::string server;
|
||||||
|
constexpr operator FullJid(this auto&& self) {
|
||||||
|
return {.username = std::forward_like<decltype(self)>(self.username), .server = std::forward_like<decltype(self)>(self.server)};
|
||||||
Ivan-lis marked this conversation as resolved
Outdated
sha512sum
commented
Extra copying if there is an rvalue. Better to forward depending on the type with which it is called. Extra copying if there is an rvalue. Better to forward depending on the type with which it is called.
|
|||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static auto Parse(std::string_view jid) -> BareJid;
|
||||||
|
friend auto ToString(const BareJid& jid) -> std::string;
|
||||||
|
|
||||||
|
constexpr auto operator==(const BareJid&) const -> bool = default;
|
||||||
|
template <typename Self>
|
||||||
|
[[nodiscard]] constexpr auto Username(this Self&& self, std::string username) -> std::decay_t<Self> {
|
||||||
|
return utils::FieldSetHelper::With<"username", BareJid>(std::forward<Self>(self), std::move(username));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Self>
|
||||||
|
[[nodiscard]] constexpr auto Server(this Self&& self, std::string server) -> std::decay_t<Self> {
|
||||||
|
return utils::FieldSetHelper::With<"server", BareJid>(std::forward<Self>(self), std::move(server));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
using JidVariant = std::variant<BareJid, BareResourceJid, FullJid>;
|
using JidVariant = std::variant<BareJid, BareResourceJid, FullJid>;
|
||||||
|
|
||||||
struct Jid : JidVariant {
|
struct Jid : JidVariant {
|
||||||
|
|
31
library/src/bind.cpp
Normal file
31
library/src/bind.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include <larra/bind.hpp>
|
||||||
|
|
||||||
|
namespace larra::xmpp::iq {
|
||||||
|
auto operator<<(xmlpp::Element* element, const Bind& bind) -> void {
|
||||||
|
element->set_attribute("xmlns", Bind::kDefaultNamespace);
|
||||||
|
|
||||||
|
if(bind.jid) {
|
||||||
|
auto* jid_el = element->add_child_element("jid");
|
||||||
|
jid_el->add_child_text(ToString(*bind.jid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[[nodiscard]] auto Bind::Parse(xmlpp::Element* element) -> Bind {
|
||||||
|
const auto* jid_node = element->get_first_child("jid");
|
||||||
|
if(!jid_node) {
|
||||||
|
SPDLOG_DEBUG("No Jid Node at Iq::Bind");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* jid_el = dynamic_cast<const xmlpp::Element*>(jid_node);
|
||||||
|
if(!jid_el) {
|
||||||
|
throw std::runtime_error("dynamic_cast to const xmlpp::Element* failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* text = jid_el->get_first_child_text();
|
||||||
|
if(!jid_el) {
|
||||||
|
throw std::runtime_error("No text at Iq::Bind jid child");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {.jid = (jid_node ? std::optional{FullJid::Parse(text->get_content())} : std::nullopt)};
|
||||||
|
}
|
||||||
|
} // namespace larra::xmpp::iq
|
Loading…
Reference in a new issue
Maybe we can change namings later