From d1711a040b927382416bb825317b2ac8cc6e1686 Mon Sep 17 00:00:00 2001 From: blankie Date: Fri, 8 Dec 2023 18:15:28 +1100 Subject: [PATCH] Move hex-related functions into their own file --- CMakeLists.txt | 2 +- client.cpp | 51 +++++-------------------------- hex.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++ hex.h | 12 ++++++++ 4 files changed, 102 insertions(+), 45 deletions(-) create mode 100644 hex.cpp create mode 100644 hex.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 74cc537..0acea2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ list(APPEND FLAGS -Werror -Wall -Wextra -Wshadow -Wpedantic -Wno-gnu-anonymous-s add_link_options(${FLAGS}) -add_executable(${PROJECT_NAME} main.cpp numberhelper.cpp config.cpp settings.cpp models.cpp client.cpp servehelper.cpp timeutils.cpp hiredis_wrapper.cpp +add_executable(${PROJECT_NAME} main.cpp numberhelper.cpp hex.cpp config.cpp settings.cpp models.cpp client.cpp servehelper.cpp timeutils.cpp hiredis_wrapper.cpp routes/home.cpp routes/css.cpp routes/user.cpp routes/status.cpp routes/tags.cpp routes/about.cpp routes/user_settings.cpp blankie/serializer.cpp blankie/escape.cpp) set_target_properties(${PROJECT_NAME} diff --git a/client.cpp b/client.cpp index 86773d8..2922e60 100644 --- a/client.cpp +++ b/client.cpp @@ -2,6 +2,7 @@ #include #include +#include "hex.h" #include "client.h" #include "models.h" #include "curlu_wrapper.h" @@ -11,8 +12,6 @@ MastodonClient mastodon_client; static void lowercase(std::string& str); static void handle_post_server(Post& post, const std::string& host); -static std::string url_encode(const std::string& in); -static inline void hexencode(char c, char out[2]); static void share_lock(CURL* curl, curl_lock_data data, curl_lock_access access, void* clientp); static void share_unlock(CURL* curl, curl_lock_data data, void* clientp); @@ -90,7 +89,7 @@ std::optional MastodonClient::get_account_by_username(std::string host, url.set(CURLUPART_SCHEME, "https"); url.set(CURLUPART_HOST, host); url.set(CURLUPART_PATH, "/api/v1/accounts/lookup"); - url.set(CURLUPART_QUERY, "acct="s + url_encode(username)); + url.set(CURLUPART_QUERY, "acct="s + percent_encode(username)); try { Account account = this->_send_request("coyote:"s + host + ":@" + username, url); account.same_server = host == account.server; @@ -111,7 +110,7 @@ std::vector MastodonClient::get_pinned_posts(std::string host, const std:: CurlUrl url; url.set(CURLUPART_SCHEME, "https"); url.set(CURLUPART_HOST, host); - url.set(CURLUPART_PATH, "/api/v1/accounts/"s + url_encode(account_id) + "/statuses"); + url.set(CURLUPART_PATH, "/api/v1/accounts/"s + percent_encode(account_id) + "/statuses"); url.set(CURLUPART_QUERY, "pinned=true"); std::vector posts = this->_send_request("coyote:"s + host + ':' + account_id + ":pinned", url); @@ -129,7 +128,7 @@ std::vector MastodonClient::get_posts(const std::string& host, const std:: CurlUrl url; url.set(CURLUPART_SCHEME, "https"); url.set(CURLUPART_HOST, host); - url.set(CURLUPART_PATH, "/api/v1/accounts/"s + url_encode(account_id) + "/statuses"); + url.set(CURLUPART_PATH, "/api/v1/accounts/"s + percent_encode(account_id) + "/statuses"); url.set(CURLUPART_QUERY, sorting_parameters[sorting_method]); if (max_id) { url.set(CURLUPART_QUERY, "max_id="s + std::move(*max_id), CURLU_URLENCODE | CURLU_APPENDQUERY); @@ -150,7 +149,7 @@ std::optional MastodonClient::get_post(const std::string& host, std::strin CurlUrl url; url.set(CURLUPART_SCHEME, "https"); url.set(CURLUPART_HOST, host); - url.set(CURLUPART_PATH, "/api/v1/statuses/"s + url_encode(std::move(id))); + url.set(CURLUPART_PATH, "/api/v1/statuses/"s + percent_encode(std::move(id))); try { Post post = this->_send_request(std::nullopt, url); handle_post_server(post, host); @@ -170,7 +169,7 @@ PostContext MastodonClient::get_post_context(const std::string& host, std::strin CurlUrl url; url.set(CURLUPART_SCHEME, "https"); url.set(CURLUPART_HOST, host); - url.set(CURLUPART_PATH, "/api/v1/statuses/"s + url_encode(std::move(id)) + "/context"); + url.set(CURLUPART_PATH, "/api/v1/statuses/"s + percent_encode(std::move(id)) + "/context"); PostContext context = this->_send_request(std::nullopt, url); for (Post& post : context.ancestors) { @@ -189,7 +188,7 @@ std::vector MastodonClient::get_tag_timeline(const std::string& host, cons CurlUrl url; url.set(CURLUPART_SCHEME, "https"); url.set(CURLUPART_HOST, host); - url.set(CURLUPART_PATH, "/api/v1/timelines/tag/"s + url_encode(tag)); + url.set(CURLUPART_PATH, "/api/v1/timelines/tag/"s + percent_encode(tag)); if (max_id) { url.set(CURLUPART_QUERY, "max_id="s + std::move(*max_id), CURLU_URLENCODE | CURLU_APPENDQUERY); } @@ -313,42 +312,6 @@ static void handle_post_server(Post& post, const std::string& host) { } } -static std::string url_encode(const std::string& in) { - std::string out; - char encoded[2]; - size_t pos = 0; - size_t last_pos = 0; - - out.reserve(in.size()); - while ((pos = in.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", pos)) != std::string::npos) { - out.append(in, last_pos, pos - last_pos); - hexencode(in[pos], encoded); - out += '%'; - out.append(encoded, 2); - pos++; - last_pos = pos; - } - - if (in.size() > last_pos) { - out.append(in, last_pos); - } - - return out; -} - -static inline void hexencode(char c, char out[2]) { - char nibble1 = (c >> 4) & 0xF; - char nibble2 = c & 0xF; - - auto hexencode = [](char nibble) { - return static_cast(nibble < 10 - ? '0' + nibble - : 'A' + nibble - 10); - }; - out[0] = hexencode(nibble1); - out[1] = hexencode(nibble2); -} - static void share_lock(CURL* curl, curl_lock_data data, curl_lock_access access, void* clientp) { (void)curl; (void)access; diff --git a/hex.cpp b/hex.cpp new file mode 100644 index 0000000..0e38ea3 --- /dev/null +++ b/hex.cpp @@ -0,0 +1,82 @@ +#include + +#include "hex.h" + +static void hex_encode(char c, char out[2]); +static inline char hex_decode(char nibble1, char nibble2); + + +std::string hex_encode(const char* in, size_t in_size) { + std::string out; + + out.reserve(in_size * 2); + for (size_t i = 0; i < in_size; i++) { + char encoded[2]; + hex_encode(in[i], encoded); + out.append(encoded, 2); + } + + return out; +} + +std::string percent_encode(std::string_view in) { + std::string out; + char encoded[2]; + size_t pos = 0; + size_t last_pos = 0; + + out.reserve(in.size()); + while ((pos = in.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", pos)) != std::string::npos) { + out.append(in, last_pos, pos - last_pos); + hex_encode(in[pos], encoded); + out += '%'; + out.append(encoded, 2); + pos++; + last_pos = pos; + } + + if (in.size() > last_pos) { + out.append(in, last_pos); + } + + return out; +} + +std::vector hex_decode(std::string_view in) { + if (in.size() % 2 != 0) { + throw std::invalid_argument("hex_decode(): hex string with an odd size passed"); + } + std::vector out; + + out.reserve(in.size() / 2); + for (size_t i = 0; i < in.size(); i += 2) { + out.push_back(hex_decode(in[i], in[i + 1])); + } + + return out; +} + + +static void hex_encode(char c, char out[2]) { + char nibble1 = (c >> 4) & 0xF; + char nibble2 = c & 0xF; + + auto hex_encode = [](char nibble) { + return static_cast(nibble < 10 + ? '0' + nibble + : 'A' + nibble - 10); + }; + out[0] = hex_encode(nibble1); + out[1] = hex_encode(nibble2); +} + +static inline char hex_decode(char nibble1, char nibble2) { + auto hex_decode = [](char nibble) { + if (nibble >= '0' && nibble <= '9') return nibble - '0'; + if (nibble >= 'a' && nibble <= 'f') return nibble - 'a' + 10; + if (nibble >= 'A' && nibble <= 'F') return nibble - 'A' + 10; + throw std::invalid_argument("hex_decode(): invalid nibble"); + }; + + return static_cast((hex_decode(nibble1) << 4) | hex_decode(nibble2)); +} diff --git a/hex.h b/hex.h new file mode 100644 index 0000000..733c5d4 --- /dev/null +++ b/hex.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +std::string hex_encode(const char* in, size_t in_size); +inline std::string hex_encode(const std::vector& in) { + return hex_encode(in.data(), in.size()); +} +std::string percent_encode(std::string_view in); + +std::vector hex_decode(std::string_view in);