Roll src/third_party/WebKit 3529d49:06e8485 (svn 202554:202555)
[chromium-blink-merge.git] / google_apis / gcm / engine / gservices_settings.cc
blob6621ff88cc362d9ffead64eb293719ec83b1e894
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 "google_apis/gcm/engine/gservices_settings.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/sha1.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "google_apis/gcm/engine/gservices_switches.h"
15 namespace {
16 // The expected time in seconds between periodic checkins.
17 const char kCheckinIntervalKey[] = "checkin_interval";
18 // The override URL to the checkin server.
19 const char kCheckinURLKey[] = "checkin_url";
20 // The MCS machine name to connect to.
21 const char kMCSHostnameKey[] = "gcm_hostname";
22 // The MCS port to connect to.
23 const char kMCSSecurePortKey[] = "gcm_secure_port";
24 // The URL to get MCS registration IDs.
25 const char kRegistrationURLKey[] = "gcm_registration_url";
27 const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days.
28 const int64 kMinimumCheckinInterval = 12 * 60 * 60; // seconds = 12 hours.
29 const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin";
30 const char kDefaultMCSHostname[] = "mtalk.google.com";
31 const int kDefaultMCSMainSecurePort = 5228;
32 const int kDefaultMCSFallbackSecurePort = 443;
33 const char kDefaultRegistrationURL[] =
34 "https://android.clients.google.com/c2dm/register3";
35 // Settings that are to be deleted are marked with this prefix in checkin
36 // response.
37 const char kDeleteSettingPrefix[] = "delete_";
38 // Settings digest starts with verison number followed by '-'.
39 const char kDigestVersionPrefix[] = "1-";
40 const char kMCSEnpointTemplate[] = "https://%s:%d";
41 const int kMaxSecurePort = 65535;
43 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) {
44 return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port);
47 // Default settings can be omitted, as GServicesSettings class provides
48 // reasonable defaults.
49 bool CanBeOmitted(const std::string& settings_name) {
50 return settings_name == kCheckinIntervalKey ||
51 settings_name == kCheckinURLKey ||
52 settings_name == kMCSHostnameKey ||
53 settings_name == kMCSSecurePortKey ||
54 settings_name == kRegistrationURLKey;
57 bool VerifyCheckinInterval(
58 const gcm::GServicesSettings::SettingsMap& settings) {
59 gcm::GServicesSettings::SettingsMap::const_iterator iter =
60 settings.find(kCheckinIntervalKey);
61 if (iter == settings.end())
62 return CanBeOmitted(kCheckinIntervalKey);
64 int64 checkin_interval = kMinimumCheckinInterval;
65 if (!base::StringToInt64(iter->second, &checkin_interval)) {
66 DVLOG(1) << "Failed to parse checkin interval: " << iter->second;
67 return false;
69 if (checkin_interval == std::numeric_limits<int64>::max()) {
70 DVLOG(1) << "Checkin interval is too big: " << checkin_interval;
71 return false;
73 if (checkin_interval < kMinimumCheckinInterval) {
74 DVLOG(1) << "Checkin interval: " << checkin_interval
75 << " is less than allowed minimum: " << kMinimumCheckinInterval;
78 return true;
81 bool VerifyMCSEndpoint(const gcm::GServicesSettings::SettingsMap& settings) {
82 std::string mcs_hostname;
83 gcm::GServicesSettings::SettingsMap::const_iterator iter =
84 settings.find(kMCSHostnameKey);
85 if (iter == settings.end()) {
86 // Because endpoint has 2 parts (hostname and port) we are defaulting and
87 // moving on with verification.
88 if (CanBeOmitted(kMCSHostnameKey))
89 mcs_hostname = kDefaultMCSHostname;
90 else
91 return false;
92 } else if (iter->second.empty()) {
93 DVLOG(1) << "Empty MCS hostname provided.";
94 return false;
95 } else {
96 mcs_hostname = iter->second;
99 int mcs_secure_port = 0;
100 iter = settings.find(kMCSSecurePortKey);
101 if (iter == settings.end()) {
102 // Simlarly we might have to default the port, when only hostname is
103 // provided.
104 if (CanBeOmitted(kMCSSecurePortKey))
105 mcs_secure_port = kDefaultMCSMainSecurePort;
106 else
107 return false;
108 } else if (!base::StringToInt(iter->second, &mcs_secure_port)) {
109 DVLOG(1) << "Failed to parse MCS secure port: " << iter->second;
110 return false;
113 if (mcs_secure_port < 0 || mcs_secure_port > kMaxSecurePort) {
114 DVLOG(1) << "Incorrect port value: " << mcs_secure_port;
115 return false;
118 GURL mcs_main_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
119 if (!mcs_main_endpoint.is_valid()) {
120 DVLOG(1) << "Invalid main MCS endpoint: "
121 << mcs_main_endpoint.possibly_invalid_spec();
122 return false;
124 GURL mcs_fallback_endpoint(
125 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
126 if (!mcs_fallback_endpoint.is_valid()) {
127 DVLOG(1) << "Invalid fallback MCS endpoint: "
128 << mcs_fallback_endpoint.possibly_invalid_spec();
129 return false;
132 return true;
135 bool VerifyCheckinURL(const gcm::GServicesSettings::SettingsMap& settings) {
136 gcm::GServicesSettings::SettingsMap::const_iterator iter =
137 settings.find(kCheckinURLKey);
138 if (iter == settings.end())
139 return CanBeOmitted(kCheckinURLKey);
141 GURL checkin_url(iter->second);
142 if (!checkin_url.is_valid()) {
143 DVLOG(1) << "Invalid checkin URL provided: " << iter->second;
144 return false;
147 return true;
150 bool VerifyRegistrationURL(
151 const gcm::GServicesSettings::SettingsMap& settings) {
152 gcm::GServicesSettings::SettingsMap::const_iterator iter =
153 settings.find(kRegistrationURLKey);
154 if (iter == settings.end())
155 return CanBeOmitted(kRegistrationURLKey);
157 GURL registration_url(iter->second);
158 if (!registration_url.is_valid()) {
159 DVLOG(1) << "Invalid registration URL provided: " << iter->second;
160 return false;
163 return true;
166 bool VerifySettings(const gcm::GServicesSettings::SettingsMap& settings) {
167 return VerifyCheckinInterval(settings) && VerifyMCSEndpoint(settings) &&
168 VerifyCheckinURL(settings) && VerifyRegistrationURL(settings);
171 } // namespace
173 namespace gcm {
175 // static
176 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() {
177 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval);
180 // static
181 const GURL GServicesSettings::DefaultCheckinURL() {
182 return GURL(kDefaultCheckinURL);
185 // static
186 std::string GServicesSettings::CalculateDigest(const SettingsMap& settings) {
187 unsigned char hash[base::kSHA1Length];
188 std::string data;
189 for (SettingsMap::const_iterator iter = settings.begin();
190 iter != settings.end();
191 ++iter) {
192 data += iter->first;
193 data += '\0';
194 data += iter->second;
195 data += '\0';
197 base::SHA1HashBytes(
198 reinterpret_cast<const unsigned char*>(&data[0]), data.size(), hash);
199 std::string digest =
200 kDigestVersionPrefix + base::HexEncode(hash, base::kSHA1Length);
201 digest = base::ToLowerASCII(digest);
202 return digest;
205 GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
206 digest_ = CalculateDigest(settings_);
209 GServicesSettings::~GServicesSettings() {
212 bool GServicesSettings::UpdateFromCheckinResponse(
213 const checkin_proto::AndroidCheckinResponse& checkin_response) {
214 if (!checkin_response.has_settings_diff()) {
215 DVLOG(1) << "Field settings_diff not set in response.";
216 return false;
219 bool settings_diff = checkin_response.settings_diff();
220 SettingsMap new_settings;
221 // Only reuse the existing settings, if we are given a settings difference.
222 if (settings_diff)
223 new_settings = settings_map();
225 for (int i = 0; i < checkin_response.setting_size(); ++i) {
226 std::string name = checkin_response.setting(i).name();
227 if (name.empty()) {
228 DVLOG(1) << "Setting name is empty";
229 return false;
232 if (settings_diff && name.find(kDeleteSettingPrefix) == 0) {
233 std::string setting_to_delete =
234 name.substr(arraysize(kDeleteSettingPrefix) - 1);
235 new_settings.erase(setting_to_delete);
236 DVLOG(1) << "Setting deleted: " << setting_to_delete;
237 } else {
238 std::string value = checkin_response.setting(i).value();
239 new_settings[name] = value;
240 DVLOG(1) << "New setting: '" << name << "' : '" << value << "'";
244 if (!VerifySettings(new_settings))
245 return false;
247 settings_.swap(new_settings);
248 digest_ = CalculateDigest(settings_);
249 return true;
252 void GServicesSettings::UpdateFromLoadResult(
253 const GCMStore::LoadResult& load_result) {
254 // No need to try to update settings when load_result is empty.
255 if (load_result.gservices_settings.empty())
256 return;
257 if (!VerifySettings(load_result.gservices_settings))
258 return;
259 std::string digest = CalculateDigest(load_result.gservices_settings);
260 if (digest != load_result.gservices_digest) {
261 DVLOG(1) << "G-services settings digest mismatch. "
262 << "Expected digest: " << load_result.gservices_digest
263 << ". Calculated digest is: " << digest;
264 return;
267 settings_ = load_result.gservices_settings;
268 digest_ = load_result.gservices_digest;
271 base::TimeDelta GServicesSettings::GetCheckinInterval() const {
272 int64 checkin_interval = kMinimumCheckinInterval;
273 SettingsMap::const_iterator iter = settings_.find(kCheckinIntervalKey);
274 if (iter == settings_.end() ||
275 !base::StringToInt64(iter->second, &checkin_interval)) {
276 checkin_interval = kDefaultCheckinInterval;
279 if (checkin_interval < kMinimumCheckinInterval)
280 checkin_interval = kMinimumCheckinInterval;
282 return base::TimeDelta::FromSeconds(checkin_interval);
285 GURL GServicesSettings::GetCheckinURL() const {
286 SettingsMap::const_iterator iter = settings_.find(kCheckinURLKey);
287 if (iter == settings_.end() || iter->second.empty())
288 return GURL(kDefaultCheckinURL);
289 return GURL(iter->second);
292 GURL GServicesSettings::GetMCSMainEndpoint() const {
293 // Get alternative hostname or use default.
294 std::string mcs_hostname;
295 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
296 if (iter != settings_.end() && !iter->second.empty())
297 mcs_hostname = iter->second;
298 else
299 mcs_hostname = kDefaultMCSHostname;
301 // Get alternative secure port or use defualt.
302 int mcs_secure_port = 0;
303 iter = settings_.find(kMCSSecurePortKey);
304 if (iter == settings_.end() || iter->second.empty() ||
305 !base::StringToInt(iter->second, &mcs_secure_port)) {
306 mcs_secure_port = kDefaultMCSMainSecurePort;
309 // If constructed address makes sense use it.
310 GURL mcs_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
311 if (mcs_endpoint.is_valid())
312 return mcs_endpoint;
314 // Otherwise use default settings.
315 return GURL(MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSMainSecurePort));
318 GURL GServicesSettings::GetMCSFallbackEndpoint() const {
319 // Get alternative hostname or use default.
320 std::string mcs_hostname;
321 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
322 if (iter != settings_.end() && !iter->second.empty())
323 mcs_hostname = iter->second;
324 else
325 mcs_hostname = kDefaultMCSHostname;
327 // If constructed address makes sense use it.
328 GURL mcs_endpoint(
329 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
330 if (mcs_endpoint.is_valid())
331 return mcs_endpoint;
333 return GURL(
334 MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSFallbackSecurePort));
337 GURL GServicesSettings::GetRegistrationURL() const {
338 SettingsMap::const_iterator iter = settings_.find(kRegistrationURLKey);
339 if (iter == settings_.end() || iter->second.empty()) {
340 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
341 if (!command_line->HasSwitch(switches::kGCMRegistrationURL))
342 return GURL(kDefaultRegistrationURL);
344 return GURL(
345 command_line->GetSwitchValueASCII(switches::kGCMRegistrationURL));
347 return GURL(iter->second);
350 } // namespace gcm