#include "gemini_response.h" #include namespace roro::gemini { namespace { std::string trim(const std::string& s) { size_t a = s.find_first_not_of(" \t"), b = s.find_last_not_of(" \t"); return a == std::string::npos ? "" : s.substr(a, b - a + 1); } std::string lower(std::string s) { for (auto& c : s) c = static_cast(std::tolower(static_cast(c))); return s; } } // namespace bool parseHeader(const std::string& line, Header& out) { if (line.size() < 2 || !std::isdigit(static_cast(line[0])) || !std::isdigit(static_cast(line[1]))) return false; if (line.size() > 2 && line[2] != ' ') return false; std::string meta = line.size() > 3 ? line.substr(3) : ""; if (meta.size() > 1024) return false; out.status = (line[0] - '0') * 10 + (line[1] - '0'); out.meta = meta; return true; } Category Header::category() const { switch (status / 10) { case 1: return Category::Input; case 2: return Category::Success; case 3: return Category::Redirect; case 4: return Category::TemporaryFailure; case 5: return Category::PermanentFailure; case 6: return Category::ClientCertificate; default: return Category::Unknown; } } std::string Header::mimeType() const { std::string type = lower(trim(meta.substr(0, meta.find(';')))); return type.empty() ? "text/gemini" : type; } std::string Header::parameter(const std::string& name) const { size_t pos = meta.find(';'); while (pos != std::string::npos) { size_t next = meta.find(';', pos + 1); std::string item = trim(meta.substr(pos + 1, next == std::string::npos ? std::string::npos : next - pos - 1)); size_t eq = item.find('='); if (eq != std::string::npos && lower(trim(item.substr(0, eq))) == lower(name)) return trim(item.substr(eq + 1)); pos = next; } return ""; } } // namespace roro::gemini