// CGI query parser for Boost.Program_Options // // Copyright Bruno Lalande 2008 // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include namespace boost { namespace program_options { namespace detail { struct escape_finder { template boost::iterator_range operator()(ForwardIterator begin, ForwardIterator end) { ForwardIterator found = find(begin, end, '%'); return (found != end || found + 3 <= end) ? make_iterator_range(found, found + 3) : make_iterator_range(end, end); } }; struct escape_formatter { template std::string operator()(const Match& match) const { if (match.end() != match.begin() + 3) return ""; std::istringstream hexa(std::string(match.begin() + 1, match.end())); int asciiCode; hexa >> std::hex >> asciiCode; return std::string(1, char(asciiCode)); } }; class BOOST_PROGRAM_OPTIONS_DECL env_var_error : public error { public: env_var_error(const std::string& name) : error(std::string("Environment variable ") .append(name) .append(" not defined")) {} }; class BOOST_PROGRAM_OPTIONS_DECL request_method_error : public error { public: request_method_error() : error("Invalid value for the REQUEST_METHOD environment variable") {} }; } // namespace detail class cgi_query_parser { public: cgi_query_parser() : m_desc(0), m_preserve_format(false), m_allow_unregistered(false) {} parsed_options run() { assert(m_desc); parsed_options result(m_desc); std::string query(_get_query()); if (query.empty()) return result; std::vector options; split(options, query, is_any_of("&")); for (std::vector::const_iterator i = options.begin(); i != options.end(); ++i) { _add_or_append(_parse_option(*i), result); } return result; } cgi_query_parser& options(const options_description& desc) { m_desc = &desc; return *this; } cgi_query_parser& preserve_format() { m_preserve_format = true; return *this; } cgi_query_parser& allow_unregistered() { m_allow_unregistered = true; return *this; } private: std::string _getenv(const char* name, const char* defaultValue = 0) { const char* result = getenv(name); if (!result && !defaultValue) throw detail::env_var_error(name); return result ? result : defaultValue; } std::string _get_query() { std::string request_method(_getenv("REQUEST_METHOD")); if (request_method == "GET") { return _getenv("QUERY_STRING"); } else if (request_method == "POST") { int length = lexical_cast(_getenv("CONTENT_LENGTH", "0")) + 1; scoped_array content(new char[length]); std::cin.get(content.get(), length); return std::string(content.get()); } throw detail::request_method_error(); } std::pair _parse_option(const std::string& option) { std::vector parts; split(parts, option, is_any_of("=")); if (parts.size() < 2) parts.push_back(""); if (!m_preserve_format) { using namespace detail; replace_all(parts[0], "+", " "); replace_all(parts[1], "+", " "); find_format_all(parts[0], escape_finder(), escape_formatter()); find_format_all(parts[1], escape_finder(), escape_formatter()); } return make_pair(parts[0], parts[1]); } void _add_or_append(const std::pair& parsed, parsed_options& options) { bool unregistered = false; if(!m_desc->find_nothrow(parsed.first, false)) { if (!m_allow_unregistered) throw unknown_option(parsed.first); unregistered = true; } for (std::vector