Home | History | Annotate | Download | only in net
      1 // Copyright 2014 The Chromium 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 "chrome/browser/net/safe_search_util.h"
      6 
      7 #include <string>
      8 #include <utility>
      9 #include <vector>
     10 
     11 #include "base/logging.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/string_piece.h"
     14 #include "base/strings/string_split.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/strings/stringprintf.h"
     17 #include "chrome/common/url_constants.h"
     18 #include "components/google/core/browser/google_util.h"
     19 #include "net/cookies/cookie_util.h"
     20 #include "net/http/http_request_headers.h"
     21 #include "net/url_request/url_request.h"
     22 #include "url/gurl.h"
     23 
     24 namespace {
     25 
     26 const char kYouTubePrefCookieName[] = "PREF";
     27 // YouTube pref flags are stored in bit masks of 31 bits each, called "f1",
     28 // "f2" etc. The Safety Mode flag is bit 58, so bit 27 in "f2".
     29 const char kYouTubePrefCookieSafetyModeFlagsEntryName[] = "f2";
     30 const int kYouTubePrefCookieSafetyModeFlagsEntryValue = (1 << 27);
     31 
     32 // Returns whether a URL parameter, |first_parameter| (e.g. foo=bar), has the
     33 // same key as the the |second_parameter| (e.g. foo=baz). Both parameters
     34 // must be in key=value form.
     35 bool HasSameParameterKey(const std::string& first_parameter,
     36                          const std::string& second_parameter) {
     37   DCHECK(second_parameter.find("=") != std::string::npos);
     38   // Prefix for "foo=bar" is "foo=".
     39   std::string parameter_prefix = second_parameter.substr(
     40       0, second_parameter.find("=") + 1);
     41   return StartsWithASCII(first_parameter, parameter_prefix, false);
     42 }
     43 
     44 // Examines the query string containing parameters and adds the necessary ones
     45 // so that SafeSearch is active. |query| is the string to examine and the
     46 // return value is the |query| string modified such that SafeSearch is active.
     47 std::string AddSafeSearchParameters(const std::string& query) {
     48   std::vector<std::string> new_parameters;
     49   std::string safe_parameter = chrome::kSafeSearchSafeParameter;
     50   std::string ssui_parameter = chrome::kSafeSearchSsuiParameter;
     51 
     52   std::vector<std::string> parameters;
     53   base::SplitString(query, '&', &parameters);
     54 
     55   std::vector<std::string>::iterator it;
     56   for (it = parameters.begin(); it < parameters.end(); ++it) {
     57     if (!HasSameParameterKey(*it, safe_parameter) &&
     58         !HasSameParameterKey(*it, ssui_parameter)) {
     59       new_parameters.push_back(*it);
     60     }
     61   }
     62 
     63   new_parameters.push_back(safe_parameter);
     64   new_parameters.push_back(ssui_parameter);
     65   return JoinString(new_parameters, '&');
     66 }
     67 
     68 bool IsYouTubePrefCookie(const net::cookie_util::ParsedRequestCookie& cookie) {
     69   return cookie.first == base::StringPiece(kYouTubePrefCookieName);
     70 }
     71 
     72 bool IsYouTubePrefCookieSafetyModeFlagsEntry(
     73     const std::pair<std::string, std::string>& pref_entry) {
     74   return pref_entry.first == kYouTubePrefCookieSafetyModeFlagsEntryName;
     75 }
     76 
     77 std::string JoinStringKeyValuePair(
     78     const base::StringPairs::value_type& key_value,
     79     char delimiter) {
     80   return key_value.first + delimiter + key_value.second;
     81 }
     82 
     83 // Does the opposite of base::SplitStringIntoKeyValuePairs() from
     84 // base/strings/string_util.h.
     85 std::string JoinStringKeyValuePairs(const base::StringPairs& pairs,
     86                                     char key_value_delimiter,
     87                                     char key_value_pair_delimiter) {
     88   if (pairs.empty())
     89     return std::string();
     90 
     91   base::StringPairs::const_iterator it = pairs.begin();
     92   std::string result = JoinStringKeyValuePair(*it, key_value_delimiter);
     93   ++it;
     94 
     95   for (; it != pairs.end(); ++it) {
     96     result += key_value_pair_delimiter;
     97     result += JoinStringKeyValuePair(*it, key_value_delimiter);
     98   }
     99 
    100   return result;
    101 }
    102 
    103 } // namespace
    104 
    105 namespace safe_search_util {
    106 
    107 // If |request| is a request to Google Web Search the function
    108 // enforces that the SafeSearch query parameters are set to active.
    109 // Sets the query part of |new_url| with the new value of the parameters.
    110 void ForceGoogleSafeSearch(const net::URLRequest* request, GURL* new_url) {
    111   if (!google_util::IsGoogleSearchUrl(request->url()) &&
    112       !google_util::IsGoogleHomePageUrl(request->url()))
    113     return;
    114 
    115   std::string query = request->url().query();
    116   std::string new_query = AddSafeSearchParameters(query);
    117   if (query == new_query)
    118     return;
    119 
    120   GURL::Replacements replacements;
    121   replacements.SetQueryStr(new_query);
    122   *new_url = request->url().ReplaceComponents(replacements);
    123 }
    124 
    125 // If |request| is a request to YouTube, enforces YouTube's Safety Mode by
    126 // adding/modifying YouTube's PrefCookie header.
    127 void ForceYouTubeSafetyMode(const net::URLRequest* request,
    128                             net::HttpRequestHeaders* headers) {
    129   if (!google_util::IsYoutubeDomainUrl(
    130           request->url(),
    131           google_util::ALLOW_SUBDOMAIN,
    132           google_util::DISALLOW_NON_STANDARD_PORTS))
    133     return;
    134 
    135   // Get the cookie string from the headers and parse it into key/value pairs.
    136   std::string cookie_string;
    137   headers->GetHeader(base::StringPiece(net::HttpRequestHeaders::kCookie),
    138                      &cookie_string);
    139   net::cookie_util::ParsedRequestCookies cookies;
    140   net::cookie_util::ParseRequestCookieLine(cookie_string, &cookies);
    141 
    142   // Find YouTube's pref cookie, or add it if it doesn't exist yet.
    143   net::cookie_util::ParsedRequestCookies::iterator pref_it =
    144       std::find_if(cookies.begin(), cookies.end(), IsYouTubePrefCookie);
    145   if (pref_it == cookies.end()) {
    146     cookies.push_back(std::make_pair(base::StringPiece(kYouTubePrefCookieName),
    147                                      base::StringPiece()));
    148     pref_it = cookies.end() - 1;
    149   }
    150 
    151   // The pref cookie's value may be quoted. If so, remove the quotes.
    152   std::string pref_string = pref_it->second.as_string();
    153   bool pref_string_quoted = false;
    154   if (pref_string.size() >= 2 &&
    155       pref_string[0] == '\"' &&
    156       pref_string[pref_string.size() - 1] == '\"') {
    157     pref_string_quoted = true;
    158     pref_string = pref_string.substr(1, pref_string.length() - 2);
    159   }
    160 
    161   // The pref cookie's value consists of key/value pairs. Parse them.
    162   base::StringPairs pref_values;
    163   base::SplitStringIntoKeyValuePairs(pref_string, '=', '&', &pref_values);
    164 
    165   // Find the "flags" entry that contains the Safety Mode flag, or add it if it
    166   // doesn't exist.
    167   base::StringPairs::iterator flag_it =
    168       std::find_if(pref_values.begin(), pref_values.end(),
    169                    IsYouTubePrefCookieSafetyModeFlagsEntry);
    170   int flag_value = 0;
    171   if (flag_it == pref_values.end()) {
    172     pref_values.push_back(
    173         std::make_pair(std::string(kYouTubePrefCookieSafetyModeFlagsEntryName),
    174                        std::string()));
    175     flag_it = pref_values.end() - 1;
    176   } else {
    177     base::HexStringToInt(base::StringPiece(flag_it->second), &flag_value);
    178   }
    179 
    180   // Set the Safety Mode bit.
    181   flag_value |= kYouTubePrefCookieSafetyModeFlagsEntryValue;
    182 
    183   // Finally, put it all back together and replace the original cookie string.
    184   flag_it->second = base::StringPrintf("%x", flag_value);
    185   pref_string = JoinStringKeyValuePairs(pref_values, '=', '&');
    186   if (pref_string_quoted) {
    187     pref_string = '\"' + pref_string + '\"';
    188   }
    189   pref_it->second = base::StringPiece(pref_string);
    190   cookie_string = net::cookie_util::SerializeRequestCookieLine(cookies);
    191   headers->SetHeader(base::StringPiece(net::HttpRequestHeaders::kCookie),
    192                      base::StringPiece(cookie_string));
    193 }
    194 
    195 }  // namespace safe_search_util
    196