diff --git a/src/ossia/protocols/coap/coap_client_protocol.cpp b/src/ossia/protocols/coap/coap_client_protocol.cpp index 8e76903f172..9527a056943 100644 --- a/src/ossia/protocols/coap/coap_client_protocol.cpp +++ b/src/ossia/protocols/coap/coap_client_protocol.cpp @@ -1,6 +1,8 @@ #include "coap_client_protocol.hpp" + #include +#include #include #include #include @@ -424,20 +426,38 @@ bool coap_client_protocol::observe(ossia::net::parameter_base& param, bool obs) } void coap_client_protocol::parse_namespace( - ossia::net::node_base& dev, std::string_view data) + ossia::net::node_base& parent, std::string_view data) { if(auto res = ossia::coap::parse_link_format(data)) { for(const auto& [name, opts] : res->resources) { - ossia::create_parameter(dev, name, "string"); + // If answer is in link format, make a new request + if(auto it = opts.find("ct"); it != opts.end()) + { + if(auto val = get_if(&it->second); val && *val == 40) + { + // Note : CoAP always gives absolute paths, e.g. given: + // GET coap://coap.me/location1 + // => + // ;... + + ossia::net::create_node(parent, name); + request_namespace(parent, m_host + name); + continue; + } + } + + ossia::create_parameter(parent, name, "string"); } } } -bool coap_client_protocol::update(ossia::net::node_base& node_base) + +void coap_client_protocol::request_namespace( + ossia::net::node_base& node_base, std::string_view r) { auto req = m_client->get( - m_host + "/.well-known/core", + r, [this, &node_base](const coap_pdu_t* received, coap_mid_t mid) -> coap_reply_action { size_t len; @@ -452,6 +472,11 @@ bool coap_client_protocol::update(ossia::net::node_base& node_base) return coap_reply_action::DeleteSession; }); +} + +bool coap_client_protocol::update(ossia::net::node_base& node_base) +{ + request_namespace(node_base, m_host + "/.well-known/core"); return true; } diff --git a/src/ossia/protocols/coap/coap_client_protocol.hpp b/src/ossia/protocols/coap/coap_client_protocol.hpp index 11db4d76956..f3dce75b5ef 100644 --- a/src/ossia/protocols/coap/coap_client_protocol.hpp +++ b/src/ossia/protocols/coap/coap_client_protocol.hpp @@ -39,6 +39,7 @@ class OSSIA_EXPORT coap_client_protocol void stop() override; private: + void request_namespace(ossia::net::node_base& root, std::string_view req); void parse_namespace(ossia::net::node_base& dev, std::string_view data); ossia::net::network_context_ptr m_context; diff --git a/src/ossia/protocols/coap/link_format_parser.cpp b/src/ossia/protocols/coap/link_format_parser.cpp index a4d59e51d89..2c9cad4a235 100644 --- a/src/ossia/protocols/coap/link_format_parser.cpp +++ b/src/ossia/protocols/coap/link_format_parser.cpp @@ -2,19 +2,85 @@ #include +#include + namespace ossia::coap { namespace { +static void decode_uri(const char* src, int src_n, char* dst, int dst_n) noexcept +{ + static constexpr auto digit = [](char c) constexpr { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + }; + + char a{}, b{}; + while(src_n > 2 && dst_n > 0) + { + if((*src == '%') && ((a = src[1]) && (b = src[2])) && (digit(a) && digit(b))) + { + if(a >= 'a') + a -= 'a' - 'A'; + if(a >= 'A') + a -= ('A' - 10); + else + a -= '0'; + if(b >= 'a') + b -= 'a' - 'A'; + if(b >= 'A') + b -= ('A' - 10); + else + b -= '0'; + *dst++ = 16 * a + b; + dst_n--; + src += 3; + src_n -= 3; + } + else if(*src == '+') + { + *dst++ = ' '; + dst_n--; + src++; + src_n--; + } + else + { + *dst++ = *src++; + src_n--; + dst_n--; + } + } + + while(src_n > 0 && dst_n > 0) + { + if(*src == '+') + { + *dst++ = ' '; + src++; + } + else + { + *dst++ = *src++; + } + dst_n--; + src_n--; + } + *dst++ = '\0'; +} + struct actions { link_format res; link_format::resource cur_resource; - void begin_resource(std::string x) + void begin_resource(std::string_view x) { - cur_resource = {.path = std::move(x), .options = {}}; + static thread_local std::string temp; + temp.clear(); + temp.resize(x.size() * 3 + 16); + decode_uri(x.data(), x.size(), temp.data(), temp.size()); + cur_resource = {.path = std::string(temp.data()), .options = {}}; } void end_resource(const auto&...) @@ -56,13 +122,13 @@ template struct as_type { }; static constexpr as_type as_string{}; -static const auto resource_identifier_char = x3::alnum | x3::char_('_') | x3::char_('/'); +static const auto resource_identifier_char = x3::alnum | x3::char_('_') | x3::char_('/') | x3::char_('%') | x3::char_('-') | x3::char_('+'); static const auto resource_identifier = as_string(+resource_identifier_char); static const auto resource_name = '<' >> resource_identifier[EVENT(begin_resource)] >> '>'; static const auto option_identifier_char = x3::alnum | x3::char_('_'); static const auto option_identifier = as_string(+option_identifier_char); -static const auto str_option = x3::lexeme['"' >> as_string(+(x3::char_ - '"')) >> '"']; +static const auto str_option = x3::lexeme['"' >> as_string(+((x3::char_ - '"') | x3::lit("\"\""))) >> '"']; static const auto num_option = x3::int64; static const auto option_value = (str_option | num_option); static const auto option = (option_identifier >> -('=' >> option_value))[EVENT(pair_option)]; @@ -86,4 +152,5 @@ std::optional parse_link_format(std::string_view str) return std::nullopt; return std::move(r.res); } + }