Austin Schuh | 24adb6b | 2015-09-06 17:37:40 -0700 | [diff] [blame^] | 1 | // Copyright (c) 2013, Matt Godbolt |
| 2 | // All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions are met: |
| 6 | // |
| 7 | // Redistributions of source code must retain the above copyright notice, this |
| 8 | // list of conditions and the following disclaimer. |
| 9 | // |
| 10 | // Redistributions in binary form must reproduce the above copyright notice, |
| 11 | // this list of conditions and the following disclaimer in the documentation |
| 12 | // and/or other materials provided with the distribution. |
| 13 | // |
| 14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 15 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 17 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| 18 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 19 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 20 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 21 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 22 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 23 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 24 | // POSSIBILITY OF SUCH DAMAGE. |
| 25 | |
| 26 | #include "seasocks/StringUtil.h" |
| 27 | #include "seasocks/util/CrackedUri.h" |
| 28 | |
| 29 | #include <algorithm> |
| 30 | #include <sstream> |
| 31 | #include <stdexcept> |
| 32 | |
| 33 | #define THROW(stuff) \ |
| 34 | do {\ |
| 35 | std::ostringstream err; \ |
| 36 | err << stuff; \ |
| 37 | throw std::runtime_error(err.str()); \ |
| 38 | } while (0); |
| 39 | |
| 40 | namespace { |
| 41 | |
| 42 | char fromHex(char c) { |
| 43 | c = tolower(c); |
| 44 | return c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10; |
| 45 | } |
| 46 | |
| 47 | std::string unescape(std::string uri) { |
| 48 | seasocks::replace(uri, "+", " "); |
| 49 | size_t pos = 0; |
| 50 | while (pos < uri.size()) { |
| 51 | pos = uri.find('%', pos); |
| 52 | if (pos == uri.npos) break; |
| 53 | if (pos + 2 > uri.size()) { |
| 54 | THROW("Truncated uri: '" << uri << "'"); |
| 55 | } |
| 56 | if (!isxdigit(uri[pos + 1]) || !isxdigit(uri[pos + 2])) { |
| 57 | THROW("Bad digit in uri: '" << uri << "'"); |
| 58 | } |
| 59 | auto hex = (fromHex(uri[pos + 1]) << 4) | fromHex(uri[pos + 2]); |
| 60 | uri = uri.substr(0, pos) + std::string(1, hex) + uri.substr(pos + 3); |
| 61 | ++pos; |
| 62 | } |
| 63 | return uri; |
| 64 | } |
| 65 | |
| 66 | } |
| 67 | |
| 68 | namespace seasocks { |
| 69 | |
| 70 | CrackedUri::CrackedUri(const std::string& uri) { |
| 71 | if (uri.empty() || uri[0] != '/') { |
| 72 | THROW("Malformed URI: '" << uri << "'"); |
| 73 | } |
| 74 | auto endOfPath = uri.find('?'); |
| 75 | std::string path; |
| 76 | std::string remainder; |
| 77 | if (endOfPath == uri.npos) { |
| 78 | path = uri.substr(1); |
| 79 | } else { |
| 80 | path = uri.substr(1, endOfPath - 1); |
| 81 | remainder = uri.substr(endOfPath + 1); |
| 82 | } |
| 83 | |
| 84 | _path = split(path, '/'); |
| 85 | std::transform(_path.begin(), _path.end(), _path.begin(), unescape); |
| 86 | |
| 87 | auto splitRemainder = split(remainder, '&'); |
| 88 | for (auto iter = splitRemainder.cbegin(); iter != splitRemainder.cend(); ++iter) { |
| 89 | if (iter->empty()) continue; |
| 90 | auto split = seasocks::split(*iter, '='); |
| 91 | std::transform(split.begin(), split.end(), split.begin(), unescape); |
| 92 | if (split.size() == 1) { |
| 93 | _queryParams.insert(std::make_pair(split[0], std::string())); |
| 94 | } else if (split.size() == 2) { |
| 95 | _queryParams.insert(std::make_pair(split[0], split[1])); |
| 96 | } else { |
| 97 | THROW("Malformed URI, two many = in query: '" << uri << "'"); |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | bool CrackedUri::hasParam(const std::string& param) const { |
| 103 | return _queryParams.find(param) != _queryParams.end(); |
| 104 | } |
| 105 | |
| 106 | std::string CrackedUri::queryParam(const std::string& param, const std::string& def) const { |
| 107 | auto found = _queryParams.find(param); |
| 108 | return found == _queryParams.end() ? def : found->second; |
| 109 | } |
| 110 | |
| 111 | std::vector<std::string> CrackedUri::allQueryParams(const std::string& param) const { |
| 112 | std::vector<std::string> params; |
| 113 | for (auto iter = _queryParams.find(param); iter != _queryParams.end() && iter->first == param; ++iter) |
| 114 | params.push_back(iter->second); |
| 115 | return params; |
| 116 | } |
| 117 | |
| 118 | CrackedUri CrackedUri::shift() const { |
| 119 | CrackedUri shifted(*this); |
| 120 | if (_path.size() > 1) { |
| 121 | shifted._path = std::vector<std::string>(_path.begin() + 1, _path.end()); |
| 122 | } else { |
| 123 | shifted._path = {""}; |
| 124 | } |
| 125 | |
| 126 | return shifted; |
| 127 | } |
| 128 | |
| 129 | } |