| // Copyright 2014 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <brillo/url_utils.h> | |
| #include <algorithm> | |
| namespace { | |
| // Given a URL string, determine where the query string starts and ends. | |
| // URLs have schema, domain and path (along with possible user name, password | |
| // and port number which are of no interest for us here) which could optionally | |
| // have a query string that is separated from the path by '?'. Finally, the URL | |
| // could also have a '#'-separated URL fragment which is usually used by the | |
| // browser as a bookmark element. So, for example: | |
| // http://server.com/path/to/object?k=v&foo=bar#fragment | |
| // Here: | |
| // http://server.com/path/to/object - is the URL of the object, | |
| // ?k=v&foo=bar - URL query string | |
| // #fragment - URL fragment string | |
| // If |exclude_fragment| is true, the function returns the start character and | |
| // the length of the query string alone. If it is false, the query string length | |
| // will include both the query string and the fragment. | |
| bool GetQueryStringPos(const std::string& url, | |
| bool exclude_fragment, | |
| size_t* query_pos, | |
| size_t* query_len) { | |
| size_t query_start = url.find_first_of("?#"); | |
| if (query_start == std::string::npos) { | |
| *query_pos = url.size(); | |
| if (query_len) | |
| *query_len = 0; | |
| return false; | |
| } | |
| *query_pos = query_start; | |
| if (query_len) { | |
| size_t query_end = url.size(); | |
| if (exclude_fragment) { | |
| if (url[query_start] == '?') { | |
| size_t pos_fragment = url.find('#', query_start); | |
| if (pos_fragment != std::string::npos) | |
| query_end = pos_fragment; | |
| } else { | |
| query_end = query_start; | |
| } | |
| } | |
| *query_len = query_end - query_start; | |
| } | |
| return true; | |
| } | |
| } // anonymous namespace | |
| namespace brillo { | |
| std::string url::TrimOffQueryString(std::string* url) { | |
| size_t query_pos; | |
| if (!GetQueryStringPos(*url, false, &query_pos, nullptr)) | |
| return std::string(); | |
| std::string query_string = url->substr(query_pos); | |
| url->resize(query_pos); | |
| return query_string; | |
| } | |
| std::string url::Combine(const std::string& url, const std::string& subpath) { | |
| return CombineMultiple(url, {subpath}); | |
| } | |
| std::string url::CombineMultiple(const std::string& url, | |
| const std::vector<std::string>& parts) { | |
| std::string result = url; | |
| if (!parts.empty()) { | |
| std::string query_string = TrimOffQueryString(&result); | |
| for (const auto& part : parts) { | |
| if (!part.empty()) { | |
| if (!result.empty() && result.back() != '/') | |
| result += '/'; | |
| size_t non_slash_pos = part.find_first_not_of('/'); | |
| if (non_slash_pos != std::string::npos) | |
| result += part.substr(non_slash_pos); | |
| } | |
| } | |
| result += query_string; | |
| } | |
| return result; | |
| } | |
| std::string url::GetQueryString(const std::string& url, bool remove_fragment) { | |
| std::string query_string; | |
| size_t query_pos, query_len; | |
| if (GetQueryStringPos(url, remove_fragment, &query_pos, &query_len)) { | |
| query_string = url.substr(query_pos, query_len); | |
| } | |
| return query_string; | |
| } | |
| data_encoding::WebParamList url::GetQueryStringParameters( | |
| const std::string& url) { | |
| // Extract the query string and remove the leading '?'. | |
| std::string query_string = GetQueryString(url, true); | |
| if (!query_string.empty() && query_string.front() == '?') | |
| query_string.erase(query_string.begin()); | |
| return data_encoding::WebParamsDecode(query_string); | |
| } | |
| std::string url::GetQueryStringValue(const std::string& url, | |
| const std::string& name) { | |
| return GetQueryStringValue(GetQueryStringParameters(url), name); | |
| } | |
| std::string url::GetQueryStringValue(const data_encoding::WebParamList& params, | |
| const std::string& name) { | |
| for (const auto& pair : params) { | |
| if (name.compare(pair.first) == 0) | |
| return pair.second; | |
| } | |
| return std::string(); | |
| } | |
| std::string url::RemoveQueryString(const std::string& url, | |
| bool remove_fragment_too) { | |
| size_t query_pos, query_len; | |
| if (!GetQueryStringPos(url, !remove_fragment_too, &query_pos, &query_len)) | |
| return url; | |
| std::string result = url.substr(0, query_pos); | |
| size_t fragment_pos = query_pos + query_len; | |
| if (fragment_pos < url.size()) { | |
| result += url.substr(fragment_pos); | |
| } | |
| return result; | |
| } | |
| std::string url::AppendQueryParam(const std::string& url, | |
| const std::string& name, | |
| const std::string& value) { | |
| return AppendQueryParams(url, {{name, value}}); | |
| } | |
| std::string url::AppendQueryParams(const std::string& url, | |
| const data_encoding::WebParamList& params) { | |
| if (params.empty()) | |
| return url; | |
| size_t query_pos, query_len; | |
| GetQueryStringPos(url, true, &query_pos, &query_len); | |
| size_t fragment_pos = query_pos + query_len; | |
| std::string result = url.substr(0, fragment_pos); | |
| if (query_len == 0) { | |
| result += '?'; | |
| } else if (query_len > 1) { | |
| result += '&'; | |
| } | |
| result += data_encoding::WebParamsEncode(params); | |
| if (fragment_pos < url.size()) { | |
| result += url.substr(fragment_pos); | |
| } | |
| return result; | |
| } | |
| bool url::HasQueryString(const std::string& url) { | |
| size_t query_pos, query_len; | |
| GetQueryStringPos(url, true, &query_pos, &query_len); | |
| return (query_len > 0); | |
| } | |
| } // namespace brillo |