| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | #include "util.hh" |
|---|
| 21 | #include "uri.hh" |
|---|
| 22 | |
|---|
| 23 | #include <boost/lexical_cast.hpp> |
|---|
| 24 | |
|---|
| 25 | const char uri::invalid::msgprefix[] = |
|---|
| 26 | "Uniform Resource Identifier (URI) invalid"; |
|---|
| 27 | |
|---|
| 28 | uri::uri(std::string str) |
|---|
| 29 | : port(-1) |
|---|
| 30 | { |
|---|
| 31 | using util::split1L; |
|---|
| 32 | using util::split1R; |
|---|
| 33 | |
|---|
| 34 | scheme = split1L(str, ":"); |
|---|
| 35 | |
|---|
| 36 | if (str.substr(0, 2) == "//") |
|---|
| 37 | { |
|---|
| 38 | str.erase(0, 2); |
|---|
| 39 | std::string authority = split1R(str, "/"); |
|---|
| 40 | userinfo = split1L(authority, "@"); |
|---|
| 41 | host = split1R(authority, ":"); |
|---|
| 42 | if (!authority.empty()) |
|---|
| 43 | { |
|---|
| 44 | if (!util::is_number(authority)) |
|---|
| 45 | throw invalid("invalid port"); |
|---|
| 46 | port = boost::lexical_cast<int>(authority); |
|---|
| 47 | } |
|---|
| 48 | } |
|---|
| 49 | |
|---|
| 50 | path = split1R(str, "?"); |
|---|
| 51 | query = str; |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | std::string uri::percent_decode(std::string s) |
|---|
| 55 | { |
|---|
| 56 | std::string rv; |
|---|
| 57 | rv.reserve(s.length()); |
|---|
| 58 | for (size_t i = 0; i < s.length(); i++) |
|---|
| 59 | { |
|---|
| 60 | if (s[i] == '%') |
|---|
| 61 | { |
|---|
| 62 | if (i + 2 >= s.length()) |
|---|
| 63 | throw uri::invalid("percent encoding " |
|---|
| 64 | "truncated"); |
|---|
| 65 | int val = 0; |
|---|
| 66 | for (size_t j = 1; j <= 2; j++) |
|---|
| 67 | { |
|---|
| 68 | val *= 16; |
|---|
| 69 | char c = s[i+j]; |
|---|
| 70 | if ('0' <= c && c <= '9') val += c-'0'; |
|---|
| 71 | else if ('a' <= c && c <= 'f') val += c-'a'+10; |
|---|
| 72 | else if ('A' <= c && c <= 'F') val += c-'A'+10; |
|---|
| 73 | else throw uri::invalid("percent encoding " |
|---|
| 74 | "invalid"); |
|---|
| 75 | } |
|---|
| 76 | rv += char(val); |
|---|
| 77 | i += 2; |
|---|
| 78 | } |
|---|
| 79 | else |
|---|
| 80 | rv += s[i]; |
|---|
| 81 | } |
|---|
| 82 | return rv; |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | std::string uri::percent_encode(std::string s) |
|---|
| 86 | { |
|---|
| 87 | std::string rv; |
|---|
| 88 | rv.reserve(s.length()); |
|---|
| 89 | for (size_t i = 0; i < s.length(); i++) |
|---|
| 90 | { |
|---|
| 91 | char c = s[i]; |
|---|
| 92 | if ( ('a' <= c && c <= 'z') || |
|---|
| 93 | ('A' <= c && c <= 'Z') || |
|---|
| 94 | ('0' <= c && c <= '9') || |
|---|
| 95 | (c == '/') || |
|---|
| 96 | c == '-' || |
|---|
| 97 | c == '.' || |
|---|
| 98 | c == '_' || |
|---|
| 99 | c == '~') |
|---|
| 100 | { |
|---|
| 101 | rv += c; |
|---|
| 102 | continue; |
|---|
| 103 | } |
|---|
| 104 | rv += '%'; |
|---|
| 105 | static const char hexdig[] = "0123456789ABCDEF"; |
|---|
| 106 | rv += hexdig[c / 16]; |
|---|
| 107 | rv += hexdig[c % 16]; |
|---|
| 108 | } |
|---|
| 109 | return rv; |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | void uri::replace_query_param(std::string name, std::string new_value) |
|---|
| 113 | { |
|---|
| 114 | remove_query_param(name); |
|---|
| 115 | if (!query.empty()) |
|---|
| 116 | query += '&'; |
|---|
| 117 | query += name + "=" + percent_encode(new_value); |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| 120 | void uri::replace_query_param(std::string name, int val) |
|---|
| 121 | { |
|---|
| 122 | std::ostringstream os; |
|---|
| 123 | os << val; |
|---|
| 124 | replace_query_param(name, os.str()); |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | void uri::remove_query_param(std::string name) |
|---|
| 128 | { |
|---|
| 129 | for (size_t ix = 0; ix < query.length(); ) |
|---|
| 130 | { |
|---|
| 131 | size_t nix = query.find("&", ix+1); |
|---|
| 132 | if (query.substr(ix, name.length()+1) == name + "=") |
|---|
| 133 | query.erase(ix, nix - ix); |
|---|
| 134 | else |
|---|
| 135 | { |
|---|
| 136 | ix = nix; |
|---|
| 137 | if (ix + 1 != 0) ix++; |
|---|
| 138 | } |
|---|
| 139 | } |
|---|
| 140 | for (size_t i = 0; i < query.length(); i++) |
|---|
| 141 | { |
|---|
| 142 | if (i > 0 && query[i] == '&' && query[i-1] == '&') |
|---|
| 143 | { |
|---|
| 144 | query.erase(i, 1); |
|---|
| 145 | i--; |
|---|
| 146 | } |
|---|
| 147 | |
|---|
| 148 | } |
|---|
| 149 | if (!query.empty() && query[query.length()-1] == '&') |
|---|
| 150 | query.erase(query.length()-1, 1); |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | std::string uri::get_query_param(std::string name) const |
|---|
| 154 | { |
|---|
| 155 | for (size_t ix = 0; ix < query.length(); ) |
|---|
| 156 | { |
|---|
| 157 | size_t nix = query.find("&", ix+1); |
|---|
| 158 | size_t eq = ix+name.length(); |
|---|
| 159 | if (eq < query.length() && query[eq] == '=' && |
|---|
| 160 | query.substr(ix, name.length()) == name) |
|---|
| 161 | { |
|---|
| 162 | std::string rv = eq + 1 < query.length() |
|---|
| 163 | ? query.substr(eq + 1, nix - eq - 1) |
|---|
| 164 | : ""; |
|---|
| 165 | return percent_decode(rv); |
|---|
| 166 | } |
|---|
| 167 | else |
|---|
| 168 | { |
|---|
| 169 | ix = nix; |
|---|
| 170 | if (ix + 1 != 0) ix++; |
|---|
| 171 | } |
|---|
| 172 | } |
|---|
| 173 | return ""; |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | std::string uri::to_string() const |
|---|
| 177 | { |
|---|
| 178 | std::ostringstream os; |
|---|
| 179 | if (!scheme.empty()) os << scheme << "://"; |
|---|
| 180 | if (!userinfo.empty()) os << userinfo << "@"; |
|---|
| 181 | if (!host.empty()) os << host; |
|---|
| 182 | if (port >= 0) os << ":" << port; |
|---|
| 183 | if (path.empty()) os << "/"; else os << path; |
|---|
| 184 | if (!query.empty()) os << "?" << query; |
|---|
| 185 | return os.str(); |
|---|
| 186 | } |
|---|