[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / google_apis / gcm / engine / gservices_settings.cc
blob4e989724b7e7333abd7e4cf53dd64e6ee841d62a
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/sha1.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
13 namespace {
14 // The expected time in seconds between periodic checkins.
15 const char kCheckinIntervalKey[] = "checkin_interval";
16 // The override URL to the checkin server.
17 const char kCheckinURLKey[] = "checkin_url";
18 // The MCS machine name to connect to.
19 const char kMCSHostnameKey[] = "gcm_hostname";
20 // The MCS port to connect to.
21 const char kMCSSecurePortKey[] = "gcm_secure_port";
22 // The URL to get MCS registration IDs.
23 const char kRegistrationURLKey[] = "gcm_registration_url";
25 const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days.
26 const int64 kMinimumCheckinInterval = 12 * 60 * 60; // seconds = 12 hours.
27 const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin";
28 const char kDefaultMCSHostname[] = "mtalk.google.com";
29 const int kDefaultMCSMainSecurePort = 5228;
30 const int kDefaultMCSFallbackSecurePort = 443;
31 const char kDefaultRegistrationURL[] =
32 "https://android.clients.google.com/c2dm/register3";
33 // Settings that are to be deleted are marked with this prefix in checkin
34 // response.
35 const char kDeleteSettingPrefix[] = "delete_";
36 // Settings digest starts with verison number followed by '-'.
37 const char kDigestVersionPrefix[] = "1-";
38 const char kMCSEnpointTemplate[] = "https://%s:%d";
39 const int kMaxSecurePort = 65535;
41 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) {
42 return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port);
45 // Default settings can be omitted, as GServicesSettings class provides
46 // reasonable defaults.
47 bool CanBeOmitted(const std::string& settings_name) {
48 return settings_name == kCheckinIntervalKey ||
49 settings_name == kCheckinURLKey ||
50 settings_name == kMCSHostnameKey ||
51 settings_name == kMCSSecurePortKey ||
52 settings_name == kRegistrationURLKey;
55 bool VerifyCheckinInterval(
56 const gcm::GServicesSettings::SettingsMap& settings) {
57 gcm::GServicesSettings::SettingsMap::const_iterator iter =
58 settings.find(kCheckinIntervalKey);
59 if (iter == settings.end())
60 return CanBeOmitted(kCheckinIntervalKey);
62 int64 checkin_interval = kMinimumCheckinInterval;
63 if (!base::StringToInt64(iter->second, &checkin_interval)) {
64 DVLOG(1) << "Failed to parse checkin interval: " << iter->second;
65 return false;
67 if (checkin_interval == std::numeric_limits<int64>::max()) {
68 DVLOG(1) << "Checkin interval is too big: " << checkin_interval;
69 return false;
71 if (checkin_interval < kMinimumCheckinInterval) {
72 DVLOG(1) << "Checkin interval: " << checkin_interval
73 << " is less than allowed minimum: " << kMinimumCheckinInterval;
76 return true;
79 bool VerifyMCSEndpoint(const gcm::GServicesSettings::SettingsMap& settings) {
80 std::string mcs_hostname;
81 gcm::GServicesSettings::SettingsMap::const_iterator iter =
82 settings.find(kMCSHostnameKey);
83 if (iter == settings.end()) {
84 // Because endpoint has 2 parts (hostname and port) we are defaulting and
85 // moving on with verification.
86 if (CanBeOmitted(kMCSHostnameKey))
87 mcs_hostname = kDefaultMCSHostname;
88 else
89 return false;
90 } else if (iter->second.empty()) {
91 DVLOG(1) << "Empty MCS hostname provided.";
92 return false;
93 } else {
94 mcs_hostname = iter->second;
97 int mcs_secure_port = 0;
98 iter = settings.find(kMCSSecurePortKey);
99 if (iter == settings.end()) {
100 // Simlarly we might have to default the port, when only hostname is
101 // provided.
102 if (CanBeOmitted(kMCSSecurePortKey))
103 mcs_secure_port = kDefaultMCSMainSecurePort;
104 else
105 return false;
106 } else if (!base::StringToInt(iter->second, &mcs_secure_port)) {
107 DVLOG(1) << "Failed to parse MCS secure port: " << iter->second;
108 return false;
111 if (mcs_secure_port < 0 || mcs_secure_port > kMaxSecurePort) {
112 DVLOG(1) << "Incorrect port value: " << mcs_secure_port;
113 return false;
116 GURL mcs_main_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
117 if (!mcs_main_endpoint.is_valid()) {
118 DVLOG(1) << "Invalid main MCS endpoint: "
119 << mcs_main_endpoint.possibly_invalid_spec();
120 return false;
122 GURL mcs_fallback_endpoint(
123 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
124 if (!mcs_fallback_endpoint.is_valid()) {
125 DVLOG(1) << "Invalid fallback MCS endpoint: "
126 << mcs_fallback_endpoint.possibly_invalid_spec();
127 return false;
130 return true;
133 bool VerifyCheckinURL(const gcm::GServicesSettings::SettingsMap& settings) {
134 gcm::GServicesSettings::SettingsMap::const_iterator iter =
135 settings.find(kCheckinURLKey);
136 if (iter == settings.end())
137 return CanBeOmitted(kCheckinURLKey);
139 GURL checkin_url(iter->second);
140 if (!checkin_url.is_valid()) {
141 DVLOG(1) << "Invalid checkin URL provided: " << iter->second;
142 return false;
145 return true;
148 bool VerifyRegistrationURL(
149 const gcm::GServicesSettings::SettingsMap& settings) {
150 gcm::GServicesSettings::SettingsMap::const_iterator iter =
151 settings.find(kRegistrationURLKey);
152 if (iter == settings.end())
153 return CanBeOmitted(kRegistrationURLKey);
155 GURL registration_url(iter->second);
156 if (!registration_url.is_valid()) {
157 DVLOG(1) << "Invalid registration URL provided: " << iter->second;
158 return false;
161 return true;
164 bool VerifySettings(const gcm::GServicesSettings::SettingsMap& settings) {
165 return VerifyCheckinInterval(settings) && VerifyMCSEndpoint(settings) &&
166 VerifyCheckinURL(settings) && VerifyRegistrationURL(settings);
169 } // namespace
171 namespace gcm {
173 // static
174 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() {
175 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval);
178 // static
179 const GURL GServicesSettings::DefaultCheckinURL() {
180 return GURL(kDefaultCheckinURL);
183 // static
184 std::string GServicesSettings::CalculateDigest(const SettingsMap& settings) {
185 unsigned char hash[base::kSHA1Length];
186 std::string data;
187 for (SettingsMap::const_iterator iter = settings.begin();
188 iter != settings.end();
189 ++iter) {
190 data += iter->first;
191 data += '\0';
192 data += iter->second;
193 data += '\0';
195 base::SHA1HashBytes(
196 reinterpret_cast<const unsigned char*>(&data[0]), data.size(), hash);
197 std::string digest =
198 kDigestVersionPrefix + base::HexEncode(hash, base::kSHA1Length);
199 digest = StringToLowerASCII(digest);
200 return digest;
203 GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
204 digest_ = CalculateDigest(settings_);
207 GServicesSettings::~GServicesSettings() {
210 bool GServicesSettings::UpdateFromCheckinResponse(
211 const checkin_proto::AndroidCheckinResponse& checkin_response) {
212 if (!checkin_response.has_settings_diff()) {
213 DVLOG(1) << "Field settings_diff not set in response.";
214 return false;
217 bool settings_diff = checkin_response.settings_diff();
218 SettingsMap new_settings;
219 // Only reuse the existing settings, if we are given a settings difference.
220 if (settings_diff)
221 new_settings = settings_map();
223 for (int i = 0; i < checkin_response.setting_size(); ++i) {
224 std::string name = checkin_response.setting(i).name();
225 if (name.empty()) {
226 DVLOG(1) << "Setting name is empty";
227 return false;
230 if (settings_diff && name.find(kDeleteSettingPrefix) == 0) {
231 std::string setting_to_delete =
232 name.substr(arraysize(kDeleteSettingPrefix) - 1);
233 new_settings.erase(setting_to_delete);
234 DVLOG(1) << "Setting deleted: " << setting_to_delete;
235 } else {
236 std::string value = checkin_response.setting(i).value();
237 new_settings[name] = value;
238 DVLOG(1) << "New setting: '" << name << "' : '" << value << "'";
242 if (!VerifySettings(new_settings))
243 return false;
245 settings_.swap(new_settings);
246 digest_ = CalculateDigest(settings_);
247 return true;
250 void GServicesSettings::UpdateFromLoadResult(
251 const GCMStore::LoadResult& load_result) {
252 // No need to try to update settings when load_result is empty.
253 if (load_result.gservices_settings.empty())
254 return;
255 if (!VerifySettings(load_result.gservices_settings))
256 return;
257 std::string digest = CalculateDigest(load_result.gservices_settings);
258 if (digest != load_result.gservices_digest) {
259 DVLOG(1) << "G-services settings digest mismatch. "
260 << "Expected digest: " << load_result.gservices_digest
261 << ". Calculated digest is: " << digest;
262 return;
265 settings_ = load_result.gservices_settings;
266 digest_ = load_result.gservices_digest;
269 base::TimeDelta GServicesSettings::GetCheckinInterval() const {
270 int64 checkin_interval = kMinimumCheckinInterval;
271 SettingsMap::const_iterator iter = settings_.find(kCheckinIntervalKey);
272 if (iter == settings_.end() ||
273 !base::StringToInt64(iter->second, &checkin_interval)) {
274 checkin_interval = kDefaultCheckinInterval;
277 if (checkin_interval < kMinimumCheckinInterval)
278 checkin_interval = kMinimumCheckinInterval;
280 return base::TimeDelta::FromSeconds(checkin_interval);
283 GURL GServicesSettings::GetCheckinURL() const {
284 SettingsMap::const_iterator iter = settings_.find(kCheckinURLKey);
285 if (iter == settings_.end() || iter->second.empty())
286 return GURL(kDefaultCheckinURL);
287 return GURL(iter->second);
290 GURL GServicesSettings::GetMCSMainEndpoint() const {
291 // Get alternative hostname or use default.
292 std::string mcs_hostname;
293 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
294 if (iter != settings_.end() && !iter->second.empty())
295 mcs_hostname = iter->second;
296 else
297 mcs_hostname = kDefaultMCSHostname;
299 // Get alternative secure port or use defualt.
300 int mcs_secure_port = 0;
301 iter = settings_.find(kMCSSecurePortKey);
302 if (iter == settings_.end() || iter->second.empty() ||
303 !base::StringToInt(iter->second, &mcs_secure_port)) {
304 mcs_secure_port = kDefaultMCSMainSecurePort;
307 // If constructed address makes sense use it.
308 GURL mcs_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
309 if (mcs_endpoint.is_valid())
310 return mcs_endpoint;
312 // Otherwise use default settings.
313 return GURL(MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSMainSecurePort));
316 GURL GServicesSettings::GetMCSFallbackEndpoint() const {
317 // Get alternative hostname or use default.
318 std::string mcs_hostname;
319 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
320 if (iter != settings_.end() && !iter->second.empty())
321 mcs_hostname = iter->second;
322 else
323 mcs_hostname = kDefaultMCSHostname;
325 // If constructed address makes sense use it.
326 GURL mcs_endpoint(
327 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
328 if (mcs_endpoint.is_valid())
329 return mcs_endpoint;
331 return GURL(
332 MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSFallbackSecurePort));
335 GURL GServicesSettings::GetRegistrationURL() const {
336 SettingsMap::const_iterator iter = settings_.find(kRegistrationURLKey);
337 if (iter == settings_.end() || iter->second.empty())
338 return GURL(kDefaultRegistrationURL);
339 return GURL(iter->second);
342 } // namespace gcm