| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // 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 |