diff --git a/examples/src/connect.cpp b/examples/src/connect.cpp index b664a65..f5ac9d3 100644 --- a/examples/src/connect.cpp +++ b/examples/src/connect.cpp @@ -8,6 +8,8 @@ #include #include +#include "larra/bind.hpp" + namespace iq = larra::xmpp::iq; auto Coroutine() -> boost::asio::awaitable { @@ -18,10 +20,14 @@ auto Coroutine() -> boost::asio::awaitable { larra::xmpp::PlainUserAccount{.jid = {.username = "test1", .server = "localhost"}, .password = "test1"}, {.useTls = larra::xmpp::client::Options::kNever}); - // TODO(unknown): // 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( + [](auto& client) -> boost::asio::awaitable { + co_await client.CreateResourceBind(); + }, + client); co_await std::visit( [](auto& client) -> boost::asio::awaitable { diff --git a/library/include/larra/bind.hpp b/library/include/larra/bind.hpp new file mode 100644 index 0000000..d43f550 --- /dev/null +++ b/library/include/larra/bind.hpp @@ -0,0 +1,54 @@ +#pragma once +#include +#include + +#include +#include +#include + +namespace larra::xmpp::iq { + +struct Bind { + static constexpr auto kDefaultName = "bind"; + static constexpr auto kDefaultNamespace = "urn:ietf:params:xml:ns:xmpp-bind"; + + std::optional jid; + + friend constexpr auto operator<<(xmlpp::Element* element, const Bind& Bind) { + 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]] static constexpr auto 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(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)}; + } +}; + +using SetBind = Set; +using ResultBind = Result; +using IqBind = Iq; + +auto MakeSetBind() { + IqBind iq_bind{std::in_place_type, SetBind{.id = "1", .payload = Bind{}}}; + return iq_bind; +} +} // namespace larra::xmpp::iq diff --git a/library/include/larra/client/client.hpp b/library/include/larra/client/client.hpp index c1f033f..7a5e855 100644 --- a/library/include/larra/client/client.hpp +++ b/library/include/larra/client/client.hpp @@ -22,7 +22,10 @@ #include #include #include + +#include "larra/bind.hpp" #include "larra/iq.hpp" +#include "larra/jid.hpp" #include "larra/roster.hpp" namespace rng = std::ranges; @@ -76,29 +79,42 @@ struct Client { [[nodiscard]] constexpr auto Jid() const -> const BareJid& { return this->jid; } - + + auto CreateResourceBind() -> boost::asio::awaitable { + SPDLOG_INFO("Send IQ: Set::Bind"); + co_await std::visit( + [&](auto&& query) -> boost::asio::awaitable { + co_await this->Send(query); + }, + ::iq::MakeSetBind()); + + auto bind_result = co_await connection.template Read<::iq::ResultBind>(); + resource_bind = std::move(bind_result.payload.jid->resource); + co_return; + } + auto UpdateListOfContacts() -> boost::asio::awaitable { SPDLOG_INFO("Send IQ: Get::Roster"); co_await std::visit( - [&](auto&& query) -> boost::asio::awaitable { - co_await this->Send(query); - }, ::iq::MakeGetRoster(jid)); - + [&](auto&& query) -> boost::asio::awaitable { + co_await this->Send(query); + }, + ::iq::MakeGetRoster(FullJid{}.Username(jid.username).Server(jid.server).Resource(resource_bind))); - //IqRoster roster = co_await connection.template Read(); - //const auto& roster_result = std::get<::iq::ResultRoster>(roster); - ::iq::ResultRoster roster_result = co_await connection.template Read<::iq::ResultRoster>(); - for (const auto& jid : roster_result.payload.items) { + // IqRoster roster = co_await connection.template Read(); + // const auto& roster_result = std::get<::iq::ResultRoster>(roster); + const auto roster_result = co_await connection.template Read<::iq::ResultRoster>(); + for(const auto& jid : roster_result.payload.items) { SPDLOG_INFO("\t'{}'", ToString(jid)); } co_return; } - private: bool active = true; XmlStream connection{}; BareJid jid; + std::string resource_bind; }; struct StartTlsNegotiationError : std::runtime_error { diff --git a/library/include/larra/roster.hpp b/library/include/larra/roster.hpp index b7b835d..a0a4fe1 100644 --- a/library/include/larra/roster.hpp +++ b/library/include/larra/roster.hpp @@ -2,10 +2,10 @@ #include #include +#include #include #include #include -#include namespace larra::xmpp::iq { @@ -24,32 +24,29 @@ struct Roster { SPDLOG_DEBUG("No items at Iq::Roster"); } - return { - .items = item_nodes - | std::views::transform([](const xmlpp::Node* node) { - auto item_element = dynamic_cast(node); - if (!item_element) { - throw std::runtime_error("Can't convert xmlpp::Node to xmlpp::Element"); - } + return {.items = item_nodes | std::views::transform([](const xmlpp::Node* node) { + auto item_element = dynamic_cast(node); + if(!item_element) { + throw std::runtime_error("Can't convert xmlpp::Node to xmlpp::Element"); + } - auto jid_ptr = item_element->get_attribute("jid"); - if (!jid_ptr) { - throw std::runtime_error("Not found attribute 'jid' for parse Roster item"); - } + auto jid_ptr = item_element->get_attribute("jid"); + if(!jid_ptr) { + throw std::runtime_error("Not found attribute 'jid' for parse Roster item"); + } - return BareJid::Parse(jid_ptr->get_value()); - }) - | std::ranges::to>()}; + return BareJid::Parse(jid_ptr->get_value()); + }) | + std::ranges::to>()}; } - }; using GetRoster = Get; using ResultRoster = Result; using IqRoster = Iq; -auto MakeGetRoster(const BareJid& jid) { - IqRoster iq_roster{std::in_place_type, GetRoster{.id="1", .from=ToString(jid), .payload=Roster{}}}; +auto MakeGetRoster(const FullJid& jid) { + IqRoster iq_roster{std::in_place_type, GetRoster{.id = "1", .from = ToString(jid), .payload = Roster{}}}; return iq_roster; }