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.
5 #include "chrome/browser/net/safe_search_util.h"
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"
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);
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);
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
;
52 std::vector
<std::string
> parameters
;
53 base::SplitString(query
, '&', ¶meters
);
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
);
63 new_parameters
.push_back(safe_parameter
);
64 new_parameters
.push_back(ssui_parameter
);
65 return JoinString(new_parameters
, '&');
68 bool IsYouTubePrefCookie(const net::cookie_util::ParsedRequestCookie
& cookie
) {
69 return cookie
.first
== base::StringPiece(kYouTubePrefCookieName
);
72 bool IsYouTubePrefCookieSafetyModeFlagsEntry(
73 const std::pair
<std::string
, std::string
>& pref_entry
) {
74 return pref_entry
.first
== kYouTubePrefCookieSafetyModeFlagsEntryName
;
77 std::string
JoinStringKeyValuePair(
78 const base::StringPairs::value_type
& key_value
,
80 return key_value
.first
+ delimiter
+ key_value
.second
;
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
) {
91 base::StringPairs::const_iterator it
= pairs
.begin();
92 std::string result
= JoinStringKeyValuePair(*it
, key_value_delimiter
);
95 for (; it
!= pairs
.end(); ++it
) {
96 result
+= key_value_pair_delimiter
;
97 result
+= JoinStringKeyValuePair(*it
, key_value_delimiter
);
105 namespace safe_search_util
{
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()))
115 std::string query
= request
->url().query();
116 std::string new_query
= AddSafeSearchParameters(query
);
117 if (query
== new_query
)
120 GURL::Replacements replacements
;
121 replacements
.SetQueryStr(new_query
);
122 *new_url
= request
->url().ReplaceComponents(replacements
);
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(
131 google_util::ALLOW_SUBDOMAIN
,
132 google_util::DISALLOW_NON_STANDARD_PORTS
))
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
),
139 net::cookie_util::ParsedRequestCookies cookies
;
140 net::cookie_util::ParseRequestCookieLine(cookie_string
, &cookies
);
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;
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);
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
);
165 // Find the "flags" entry that contains the Safety Mode flag, or add it if it
167 base::StringPairs::iterator flag_it
=
168 std::find_if(pref_values
.begin(), pref_values
.end(),
169 IsYouTubePrefCookieSafetyModeFlagsEntry
);
171 if (flag_it
== pref_values
.end()) {
172 pref_values
.push_back(
173 std::make_pair(std::string(kYouTubePrefCookieSafetyModeFlagsEntryName
),
175 flag_it
= pref_values
.end() - 1;
177 base::HexStringToInt(base::StringPiece(flag_it
->second
), &flag_value
);
180 // Set the Safety Mode bit.
181 flag_value
|= kYouTubePrefCookieSafetyModeFlagsEntryValue
;
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
+ '\"';
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
));
195 } // namespace safe_search_util