From f0267d7a5cebef0195b500ca08963c9b9a05a4e8 Mon Sep 17 00:00:00 2001 From: ThoNohT Date: Wed, 17 Jun 2026 17:34:07 +0200 Subject: [PATCH] Implement url en/decoding --- src/get_handler.c | 2 +- src/opds_handler.c | 2 +- src/path.c | 102 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 103 insertions(+), 3 deletions(-) diff --git a/src/get_handler.c b/src/get_handler.c index 2cf4120..8c4d920 100644 --- a/src/get_handler.c +++ b/src/get_handler.c @@ -1,7 +1,7 @@ static enum MHD_Result handle_get(struct MHD_Connection *connection, SV url, Settings settings, SV username) { noh_arena_save(&temp); Noh_String fs_path = build_path(false, false, &settings.base_path, &username, &url); - noh_string_replace(&fs_path, '+', ' '); + url_decode(&fs_path); noh_log(NOH_INFO, "Physical path: "Nsv_Fmt, Nsv_Arg(fs_path)); diff --git a/src/opds_handler.c b/src/opds_handler.c index 52a666e..6f1c54f 100644 --- a/src/opds_handler.c +++ b/src/opds_handler.c @@ -49,7 +49,7 @@ static Noh_String create_feed_response(Library_Entry self_path, SV url, Library_ static enum MHD_Result handle_opds(struct MHD_Connection *connection, SV url, Settings settings, SV username) { noh_arena_save(&temp); Noh_String fs_path = build_path(false, false, &settings.base_path, &username, &url); - noh_string_replace(&fs_path, '+', ' '); + url_decode(&fs_path); Noh_String fs_path_cstr = fs_path; noh_string_append_null(&fs_path_cstr); diff --git a/src/path.c b/src/path.c index 3651043..0d5f857 100644 --- a/src/path.c +++ b/src/path.c @@ -1,3 +1,103 @@ +static inline bool byte_is_url_safe(char c) { + return (c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <+ '9') + || (c >= '(' && c <= '*') + || c == '!' || c == '-' + || c == '.' || c == '_'; +} + +// Determines if a string needs to be URL encoded. +// Sets the number of unsafe characters that need to be replaced by a hexadecimal format. +static bool needs_encoding(Noh_String string, int *unsafe_count) { + *unsafe_count = 0; + bool safe = true ; + for (size_t i = 0; i < string.count; i++) { + bool needs_encode = byte_is_url_safe(string.elems[i]); + if (needs_encode || string.elems[i] == ' ') safe = false; + if (needs_encode) (*unsafe_count)++; + } + + return safe; +} + +static inline char to_char_lower(int value) { + value &= 0XF; + value += '0'; + if (value > '9') value += ('a' - ('9' + 1)); + + return (char)value; +} + +static inline int from_char(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'z') return c - 'a'; + if (c >= 'A' && c <= 'Z') return c - 'A'; + + return 0xFF; +} + +void url_encode(Noh_String *string) { + int unsafe_count = 0; + if (!needs_encoding(*string, &unsafe_count)) return; + + // TODO: Use the calculated size to allocate exactly enough memory? + // size_t new_string_size = string->count + unsafe_count * 2 + 1; + Noh_String result = {0}; + + for (size_t i = 0; i < string->count; i++) { + char cur = string->elems[i]; + if (cur == ' ') noh_da_append(&result, '+'); + + if (!byte_is_url_safe(cur)) { + noh_da_append(&result, '%'); + noh_da_append(&result, to_char_lower(cur >> 4)); + noh_da_append(&result, to_char_lower(cur)); + } + } + + noh_string_free(string); + string->elems = result.elems; + string->count = result.count; + string->capacity = result.capacity; +} + +void url_decode(Noh_String *string) { + size_t read = 0; + size_t write = 0; + + while (read < string->count) { + char c = string->elems[read++]; + if (c == '+') { + string->elems[write++] = ' '; + continue; + } + + if (c == '%') { + int h1 = string->elems[read]; + int h2 = string->elems[read + 1]; + + // If valid hex chars, add it. + if ((h1 | h2) != 0xFF) { + string->elems[write] = (char)((h1 << 4) | h2); + read += 2; + continue; + } + + // Fallback to just adding the '%'. + } + + // Just copy the character. + string->elems[write++] = c; + } + + // Zero-out the rest. + string->count = read; + for (size_t i = read; i < string->count; i++) { + string->elems[i] = 0; + } +} + #define build_path(url, first, ...) build_path_((url), (first), __VA_ARGS__, NULL) static Noh_String build_path_(bool url, bool go_up, Noh_String_View *first, ...) { va_list args; @@ -23,7 +123,7 @@ static Noh_String build_path_(bool url, bool go_up, Noh_String_View *first, ...) } va_end(args); - if (url) noh_string_replace(&result, ' ', '+'); + if (url) url_encode(&result); return result; }