• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
GetQueryStringPos(const std::string & url,bool exclude_fragment,size_t * query_pos,size_t * query_len)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 
TrimOffQueryString(std::string * url)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 
Combine(const std::string & url,const std::string & subpath)66 std::string url::Combine(const std::string& url, const std::string& subpath) {
67   return CombineMultiple(url, {subpath});
68 }
69 
CombineMultiple(const std::string & url,const std::vector<std::string> & parts)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 
GetQueryString(const std::string & url,bool remove_fragment)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 
GetQueryStringParameters(const std::string & url)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 
GetQueryStringValue(const std::string & url,const std::string & name)107 std::string url::GetQueryStringValue(const std::string& url,
108                                      const std::string& name) {
109   return GetQueryStringValue(GetQueryStringParameters(url), name);
110 }
111 
GetQueryStringValue(const data_encoding::WebParamList & params,const std::string & name)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 
RemoveQueryString(const std::string & url,bool remove_fragment_too)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 
AppendQueryParam(const std::string & url,const std::string & name,const std::string & value)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 
AppendQueryParams(const std::string & url,const data_encoding::WebParamList & params)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 
HasQueryString(const std::string & url)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