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