aboutsummaryrefslogtreecommitdiffstats
path: root/brillo/url_utils.cc
blob: eba7db84df1a7b106326ab49aaaa2553186fc4ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// 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