Home | History | Annotate | Download | only in brillo
      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