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