diff --git a/sys/Makefile.dep b/sys/Makefile.dep index b24eef8d08..5a4501397c 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -703,6 +703,10 @@ ifneq (,$(filter nanocoap_cache,$(USEMODULE))) USEMODULE += hashes endif +ifneq (,$(filter nanocoap_link_format,$(USEMODULE))) + USEMODULE += fmt +endif + ifneq (,$(filter nanocoap_vfs,$(USEMODULE))) USEMODULE += nanocoap_sock USEMODULE += vfs diff --git a/sys/include/net/nanocoap/link_format.h b/sys/include/net/nanocoap/link_format.h new file mode 100644 index 0000000000..e0ea047d32 --- /dev/null +++ b/sys/include/net/nanocoap/link_format.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup net_nanosock + * @brief NanoCoAP Link Format helper functions + * + * @{ + * + * @file + * @brief NanoCoAP Link Format ([RFC 6690](https://www.rfc-editor.org/rfc/rfc6690.html)) + * helper functions + * + * @author Benjamin Valentin + */ +#ifndef NET_NANOCOAP_LINK_FORMAT_H +#define NET_NANOCOAP_LINK_FORMAT_H + +#include "net/nanocoap_sock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Callback function called for each resource on the directory + * + * @param[in] entry Resource entry from the server + * @param[in] ctx Optional function context + * + * @returns 0 on success + * @returns <0 on error + */ +typedef int (*coap_link_format_handler_t)(char *entry, void *ctx); + +/** + * @brief Downloads the resource behind @p path via blockwise GET + * + * @param[in] sock Connection to the server + * @param[in] path path of the resource + * @param[in] cb Callback to execute for each resource entry + * @param[in] arg Optional callback argument + * + * @returns 0 on success + * @returns <0 on error + */ +int nanocoap_link_format_get(nanocoap_sock_t *sock, const char *path, + coap_link_format_handler_t cb, void *arg); + +/** + * @brief Downloads the resource behind @p url via blockwise GET + * + * @param[in] url URL to the resource + * @param[in] cb Callback to execute for each resource entry + * @param[in] arg Optional callback argument + * + * @returns 0 on success + * @returns <0 on error + */ +int nanocoap_link_format_get_url(const char *url, + coap_link_format_handler_t cb, void *arg); + +#ifdef __cplusplus +} +#endif +#endif /* NET_NANOCOAP_LINK_FORMAT_H */ +/** @} */ diff --git a/sys/net/application_layer/nanocoap/link_format.c b/sys/net/application_layer/nanocoap/link_format.c new file mode 100644 index 0000000000..c80db58dd9 --- /dev/null +++ b/sys/net/application_layer/nanocoap/link_format.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup net_nanocoap + * @{ + * + * @file + * @brief NanoCoAP Link Format parser + * + * @author Benjamin Valentin + * + * @} + */ + +#include "fmt.h" +#include "net/nanocoap/link_format.h" +#include "net/nanocoap_sock.h" +#include "net/sock/util.h" + +struct dir_list_ctx { + char *buf; + char *cur; + char *end; + coap_link_format_handler_t cb; + void *ctx; + char esc_buf[2]; + uint8_t esc_idx; +}; + +static int _dirlist_cb(void *arg, size_t offset, uint8_t *buf, size_t len, int more) +{ + (void)offset; + + struct dir_list_ctx *ctx = arg; + + char *end = (char *)buf + len; + for (char *c = (char *)buf; c != end; ++c) { + + /* start of escape sequence */ + if (*c == '%') { + ctx->esc_idx = 1; + continue; + } + if (ctx->esc_idx) { + /* fill escape buffer */ + ctx->esc_buf[ctx->esc_idx - 1] = *c; + if (++ctx->esc_idx == 3) { + ctx->esc_idx = 0; + *c = scn_u32_hex(ctx->esc_buf, 2); + } else { + continue; + } + } + + if (*c == ',' || ctx->cur == ctx->end) { + int res; + *ctx->cur = 0; + res = ctx->cb(ctx->buf, ctx->ctx); + ctx->cur = ctx->buf; + if (res < 0) { + return res; + } + } else { + *ctx->cur++ = *c; + } + } + + if (!more) { + *ctx->cur = 0; + return ctx->cb(ctx->buf, ctx->ctx); + } + + return 0; +} + +int nanocoap_link_format_get(nanocoap_sock_t *sock, const char *path, + coap_link_format_handler_t cb, void *arg) +{ + char buffer[CONFIG_NANOCOAP_QS_MAX]; + struct dir_list_ctx ctx = { + .buf = buffer, + .end = buffer + sizeof(buffer), + .cur = buffer, + .cb = cb, + .ctx = arg, + }; + return nanocoap_sock_get_blockwise(sock, path, CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT, + _dirlist_cb, &ctx); +} + +int nanocoap_link_format_get_url(const char *url, coap_link_format_handler_t cb, void *arg) +{ + nanocoap_sock_t sock; + int res = nanocoap_sock_url_connect(url, &sock); + if (res) { + return res; + } + + res = nanocoap_link_format_get(&sock, sock_urlpath(url), cb, arg); + nanocoap_sock_close(&sock); + + return res; +}