/* * Copyright (C) 2016 Charybdis Development Team * Copyright (C) 2016 Jason Volk * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include namespace spirit = boost::spirit; namespace qi = spirit::qi; namespace karma = spirit::karma; namespace ascii = qi::ascii; using qi::lit; using qi::string; using qi::char_; using qi::short_; using qi::ushort_; using qi::int_; using qi::long_; using qi::repeat; using qi::omit; using qi::raw; using qi::attr; using qi::eps; using qi::attr_cast; using karma::maxwidth; namespace ircd::rfc3986 { template struct grammar; } template struct ircd::rfc3986::grammar :qi::grammar { template using rule = qi::rule; rule<> NUL { lit('\0') ,"nul" }; rule<> SP { lit('\x20') ,"space" }; rule<> HT { lit('\x09') ,"horizontal tab" }; rule<> CR { lit('\x0D') ,"carriage return" }; rule<> LF { lit('\x0A') ,"line feed" }; rule<> CRLF { CR >> LF ,"carriage return, line feed" }; rule<> ws { SP | HT ,"whitespace" }; /* rule authority { //TODO: https://tools.ietf.org/html/rfc3986#section-3.2 -('/' >> '/') >> raw[*(char_ - '/')] >> '/' }; */ const rule<> port { ushort_ ,"port" }; const rule<> ip6_address { //TODO: XXX *char_("0-9a-fA-F:") ,"ip6 address" }; const rule<> ip6_literal { '[' >> ip6_address >> ']' ,"ip6 literal" }; const rule<> dns_name { ip6_literal | *(char_ - ':') ,"dns name" }; grammar() :grammar::base_type{rule<>{}} {} }; struct ircd::rfc3986::parser :grammar { string_view operator()(const string_view &url) const; } const ircd::rfc3986::parser; ircd::string_view ircd::rfc3986::parser::operator()(const string_view &url) const try { string_view out; const char *start{url.data()}; const char *const stop{url.data() + url.size()}; //qi::parse(start, stop, , out); return out; } catch(const qi::expectation_failure &e) { auto rule { ircd::string(e.what_) }; throw error { "Not a valid url because of an invalid %s.", between(rule, '<', '>') }; } struct ircd::rfc3986::encoder :karma::grammar { void throw_illegal() { throw encoding_error("Generator Protection: urlencode"); } karma::rule url_encoding { *(karma::char_("A-Za-z0-9") | (karma::lit('%') << karma::hex)) ,"url encoding" }; encoder(): encoder::base_type{url_encoding} {} } const ircd::rfc3986::encoder; ircd::string_view ircd::rfc3986::encode(const string_view &url, const mutable_buffer &buf) { char *out{data(buf)}; karma::generate(out, maxwidth(size(buf))[encoder], url); return string_view{data(buf), size_t(std::distance(data(buf), out))}; } struct ircd::rfc3986::decoder :qi::grammar { template using rule = qi::rule; rule<> url_illegal { char_(0x00, 0x1f) ,"url illegal" }; rule url_encodable { char_("A-Za-z0-9") ,"url encodable character" }; rule urlencoded_character { '%' > qi::uint_parser{} ,"urlencoded character" }; rule url_decode { *((char_ - '%') | urlencoded_character) ,"urldecode" }; decoder(): decoder::base_type { url_decode } {} } const ircd::rfc3986::decoder; ircd::string_view ircd::rfc3986::decode(const string_view &url, const mutable_buffer &buf) try { const char *start{url.data()}, *const stop { start + std::min(size(url), size(buf)) }; mutable_buffer mb{data(buf), size_t(0)}; qi::parse(start, stop, eps > decoder, mb); return string_view{data(mb), size(mb)}; } catch(const qi::expectation_failure &e) { const auto rule { ircd::string(e.what_) }; throw decoding_error { "I require a valid urlencoded %s. You sent %zu invalid chars starting with `%s'.", between(rule, "<", ">"), size_t(e.last - e.first), string_view{e.first, e.last} }; }