1 // Copyright 2014 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <brillo/url_utils.h> 6 7 #include <algorithm> 8 9 namespace { 10 // Given a URL string, determine where the query string starts and ends. 11 // URLs have schema, domain and path (along with possible user name, password 12 // and port number which are of no interest for us here) which could optionally 13 // have a query string that is separated from the path by '?'. Finally, the URL 14 // could also have a '#'-separated URL fragment which is usually used by the 15 // browser as a bookmark element. So, for example: 16 // http://server.com/path/to/object?k=v&foo=bar#fragment 17 // Here: 18 // http://server.com/path/to/object - is the URL of the object, 19 // ?k=v&foo=bar - URL query string 20 // #fragment - URL fragment string 21 // If |exclude_fragment| is true, the function returns the start character and 22 // the length of the query string alone. If it is false, the query string length 23 // will include both the query string and the fragment. 24 bool GetQueryStringPos(const std::string& url, 25 bool exclude_fragment, 26 size_t* query_pos, 27 size_t* query_len) { 28 size_t query_start = url.find_first_of("?#"); 29 if (query_start == std::string::npos) { 30 *query_pos = url.size(); 31 if (query_len) 32 *query_len = 0; 33 return false; 34 } 35 36 *query_pos = query_start; 37 if (query_len) { 38 size_t query_end = url.size(); 39 40 if (exclude_fragment) { 41 if (url[query_start] == '?') { 42 size_t pos_fragment = url.find('#', query_start); 43 if (pos_fragment != std::string::npos) 44 query_end = pos_fragment; 45 } else { 46 query_end = query_start; 47 } 48 } 49 *query_len = query_end - query_start; 50 } 51 return true; 52 } 53 } // anonymous namespace 54 55 namespace brillo { 56 57 std::string url::TrimOffQueryString(std::string* url) { 58 size_t query_pos; 59 if (!GetQueryStringPos(*url, false, &query_pos, nullptr)) 60 return std::string(); 61 std::string query_string = url->substr(query_pos); 62 url->resize(query_pos); 63 return query_string; 64 } 65 66 std::string url::Combine(const std::string& url, const std::string& subpath) { 67 return CombineMultiple(url, {subpath}); 68 } 69 70 std::string url::CombineMultiple(const std::string& url, 71 const std::vector<std::string>& parts) { 72 std::string result = url; 73 if (!parts.empty()) { 74 std::string query_string = TrimOffQueryString(&result); 75 for (const auto& part : parts) { 76 if (!part.empty()) { 77 if (!result.empty() && result.back() != '/') 78 result += '/'; 79 size_t non_slash_pos = part.find_first_not_of('/'); 80 if (non_slash_pos != std::string::npos) 81 result += part.substr(non_slash_pos); 82 } 83 } 84 result += query_string; 85 } 86 return result; 87 } 88 89 std::string url::GetQueryString(const std::string& url, bool remove_fragment) { 90 std::string query_string; 91 size_t query_pos, query_len; 92 if (GetQueryStringPos(url, remove_fragment, &query_pos, &query_len)) { 93 query_string = url.substr(query_pos, query_len); 94 } 95 return query_string; 96 } 97 98 data_encoding::WebParamList url::GetQueryStringParameters( 99 const std::string& url) { 100 // Extract the query string and remove the leading '?'. 101 std::string query_string = GetQueryString(url, true); 102 if (!query_string.empty() && query_string.front() == '?') 103 query_string.erase(query_string.begin()); 104 return data_encoding::WebParamsDecode(query_string); 105 } 106 107 std::string url::GetQueryStringValue(const std::string& url, 108 const std::string& name) { 109 return GetQueryStringValue(GetQueryStringParameters(url), name); 110 } 111 112 std::string url::GetQueryStringValue(const data_encoding::WebParamList& params, 113 const std::string& name) { 114 for (const auto& pair : params) { 115 if (name.compare(pair.first) == 0) 116 return pair.second; 117 } 118 return std::string(); 119 } 120 121 std::string url::RemoveQueryString(const std::string& url, 122 bool remove_fragment_too) { 123 size_t query_pos, query_len; 124 if (!GetQueryStringPos(url, !remove_fragment_too, &query_pos, &query_len)) 125 return url; 126 std::string result = url.substr(0, query_pos); 127 size_t fragment_pos = query_pos + query_len; 128 if (fragment_pos < url.size()) { 129 result += url.substr(fragment_pos); 130 } 131 return result; 132 } 133 134 std::string url::AppendQueryParam(const std::string& url, 135 const std::string& name, 136 const std::string& value) { 137 return AppendQueryParams(url, {{name, value}}); 138 } 139 140 std::string url::AppendQueryParams(const std::string& url, 141 const data_encoding::WebParamList& params) { 142 if (params.empty()) 143 return url; 144 size_t query_pos, query_len; 145 GetQueryStringPos(url, true, &query_pos, &query_len); 146 size_t fragment_pos = query_pos + query_len; 147 std::string result = url.substr(0, fragment_pos); 148 if (query_len == 0) { 149 result += '?'; 150 } else if (query_len > 1) { 151 result += '&'; 152 } 153 result += data_encoding::WebParamsEncode(params); 154 if (fragment_pos < url.size()) { 155 result += url.substr(fragment_pos); 156 } 157 return result; 158 } 159 160 bool url::HasQueryString(const std::string& url) { 161 size_t query_pos, query_len; 162 GetQueryStringPos(url, true, &query_pos, &query_len); 163 return (query_len > 0); 164 } 165 166 } // namespace brillo 167