| // Copyright (c) 2013, Matt Godbolt |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "seasocks/StringUtil.h" |
| #include "seasocks/util/CrackedUri.h" |
| |
| #include <algorithm> |
| #include <sstream> |
| #include <stdexcept> |
| |
| #define THROW(stuff) \ |
| do {\ |
| std::ostringstream err; \ |
| err << stuff; \ |
| throw std::runtime_error(err.str()); \ |
| } while (0); |
| |
| namespace { |
| |
| char fromHex(char c) { |
| c = tolower(c); |
| return c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10; |
| } |
| |
| std::string unescape(std::string uri) { |
| seasocks::replace(uri, "+", " "); |
| size_t pos = 0; |
| while (pos < uri.size()) { |
| pos = uri.find('%', pos); |
| if (pos == uri.npos) break; |
| if (pos + 2 > uri.size()) { |
| THROW("Truncated uri: '" << uri << "'"); |
| } |
| if (!isxdigit(uri[pos + 1]) || !isxdigit(uri[pos + 2])) { |
| THROW("Bad digit in uri: '" << uri << "'"); |
| } |
| auto hex = (fromHex(uri[pos + 1]) << 4) | fromHex(uri[pos + 2]); |
| uri = uri.substr(0, pos) + std::string(1, hex) + uri.substr(pos + 3); |
| ++pos; |
| } |
| return uri; |
| } |
| |
| } |
| |
| namespace seasocks { |
| |
| CrackedUri::CrackedUri(const std::string& uri) { |
| if (uri.empty() || uri[0] != '/') { |
| THROW("Malformed URI: '" << uri << "'"); |
| } |
| auto endOfPath = uri.find('?'); |
| std::string path; |
| std::string remainder; |
| if (endOfPath == uri.npos) { |
| path = uri.substr(1); |
| } else { |
| path = uri.substr(1, endOfPath - 1); |
| remainder = uri.substr(endOfPath + 1); |
| } |
| |
| _path = split(path, '/'); |
| std::transform(_path.begin(), _path.end(), _path.begin(), unescape); |
| |
| auto splitRemainder = split(remainder, '&'); |
| for (auto iter = splitRemainder.cbegin(); iter != splitRemainder.cend(); ++iter) { |
| if (iter->empty()) continue; |
| auto split = seasocks::split(*iter, '='); |
| std::transform(split.begin(), split.end(), split.begin(), unescape); |
| if (split.size() == 1) { |
| _queryParams.insert(std::make_pair(split[0], std::string())); |
| } else if (split.size() == 2) { |
| _queryParams.insert(std::make_pair(split[0], split[1])); |
| } else { |
| THROW("Malformed URI, two many = in query: '" << uri << "'"); |
| } |
| } |
| } |
| |
| bool CrackedUri::hasParam(const std::string& param) const { |
| return _queryParams.find(param) != _queryParams.end(); |
| } |
| |
| std::string CrackedUri::queryParam(const std::string& param, const std::string& def) const { |
| auto found = _queryParams.find(param); |
| return found == _queryParams.end() ? def : found->second; |
| } |
| |
| std::vector<std::string> CrackedUri::allQueryParams(const std::string& param) const { |
| std::vector<std::string> params; |
| for (auto iter = _queryParams.find(param); iter != _queryParams.end() && iter->first == param; ++iter) |
| params.push_back(iter->second); |
| return params; |
| } |
| |
| CrackedUri CrackedUri::shift() const { |
| CrackedUri shifted(*this); |
| if (_path.size() > 1) { |
| shifted._path = std::vector<std::string>(_path.begin() + 1, _path.end()); |
| } else { |
| shifted._path = {""}; |
| } |
| |
| return shifted; |
| } |
| |
| } |