|  | // 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 "buffet/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 buffet { | 
|  |  | 
|  | 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 buffet |