blob: 1da757fd45d37047b9f65cd5c5e4be41b8355159 [file] [log] [blame]
Austin Schuh24adb6b2015-09-06 17:37:40 -07001// 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
40namespace {
41
42char fromHex(char c) {
43 c = tolower(c);
44 return c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
45}
46
47std::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
68namespace seasocks {
69
70CrackedUri::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
102bool CrackedUri::hasParam(const std::string& param) const {
103 return _queryParams.find(param) != _queryParams.end();
104}
105
106std::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
111std::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
118CrackedUri 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}