| // 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 |