From 6232d13f5321b87ddf12a1aa36b4545da45f173d Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Wed, 17 Nov 2021 22:34:57 +0100 Subject: Travel3: Simple image and video display site Reads the images and videos from filesystem and builds a site in memroy. --- src/urlutil.cc | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/urlutil.cc (limited to 'src/urlutil.cc') diff --git a/src/urlutil.cc b/src/urlutil.cc new file mode 100644 index 0000000..00ec713 --- /dev/null +++ b/src/urlutil.cc @@ -0,0 +1,138 @@ +#include "common.hh" + +#include "urlutil.hh" + +#include + +namespace url { + +namespace { + +constexpr char kHex[] = "0123456789ABCDEF"; + +std::optional unhex(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return 10 + (c - 'A'); + if (c >= 'a' && c <= 'f') + return 10 + (c - 'a'); + return std::nullopt; +} + +bool is_unreserved(char c) { + if (c >= 'A' && c <= 'Z') + return true; + if (c >= 'a' && c <= 'z') + return true; + if (c >= '0' && c <= '9') + return true; + return c == '-' || c == '_' || c == '.' || c == '~'; +} + +std::string query_unescape(std::string_view str) { + std::string ret; + size_t start = 0; + while (true) { + auto next = str.find('+', start); + if (next == std::string::npos) { + unescape(str.substr(start), ret); + break; + } + unescape(str.substr(start, next - start), ret); + ret.push_back(' '); + start = next + 1; + } + return ret; +} + +} // namespace + +std::string escape(std::string_view str, EscapeFlags flags) { + std::string out; + escape(str, out, flags); + return out; +} + +void escape(std::string_view str, std::string& out, EscapeFlags flags) { + out.reserve(out.size() + str.size()); + bool const keep_slash = + (flags & EscapeFlags::KEEP_SLASH) == EscapeFlags::KEEP_SLASH; + for (char c : str) { + if (is_unreserved(c) || (c == '/' && keep_slash)) { + out.push_back(c); + } else { + out.push_back('%'); + out.push_back(kHex[(c & 0xff) >> 4]); + out.push_back(kHex[(c & 0xff) & 0xf]); + } + } +} + +std::string unescape(std::string_view str) { + std::string ret; + unescape(str, ret); + return ret; +} + +void unescape(std::string_view str, std::string& out) { + out.reserve(out.size() + str.size()); + + size_t last = 0; + while (true) { + auto next = str.find('%', last); + if (next == std::string::npos || next + 3 > str.size()) + break; + auto a = unhex(str[next + 1]); + auto b = unhex(str[next + 2]); + if (a && b) { + out.append(str, last, next - last); + out.push_back(a.value() << 4 | b.value()); + } else { + // Keep invalid escape sequences as-is. + out.append(str, last, next + 3 - last); + } + last = next + 3; + } + out.append(str, last); +} + +void split_and_unescape_path_and_query( + std::string_view url, + std::string& path, + std::unordered_map& query) { + auto start = url.find('?'); + if (start == std::string_view::npos) { + path = unescape(url); + query.clear(); + } else { + path = unescape(url.substr(0, start)); + query = expand_and_unescape_query(url.substr(start + 1)); + } +} + +std::unordered_map expand_and_unescape_query( + std::string_view query) { + std::unordered_map ret; + size_t start = 0; + while (true) { + auto next = query.find('&', start); + auto pair = next == std::string::npos + ? query.substr(start) + : query.substr(start, next - start); + auto eq = pair.find('='); + if (eq == std::string::npos) { + if (!pair.empty()) + ret.emplace(query_unescape(pair), std::string()); + } else { + ret.emplace(query_unescape(pair.substr(0, eq)), + query_unescape(pair.substr(eq + 1))); + } + if (next == std::string::npos) + break; + start = next + 1; + } + return ret; +} + +} // namespace url -- cgit v1.2.3-70-g09d2