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"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
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
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
;
67 if (checkin_interval
== std::numeric_limits
<int64
>::max()) {
68 DVLOG(1) << "Checkin interval is too big: " << checkin_interval
;
71 if (checkin_interval
< kMinimumCheckinInterval
) {
72 DVLOG(1) << "Checkin interval: " << checkin_interval
73 << " is less than allowed minimum: " << kMinimumCheckinInterval
;
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
;
90 } else if (iter
->second
.empty()) {
91 DVLOG(1) << "Empty MCS hostname provided.";
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
102 if (CanBeOmitted(kMCSSecurePortKey
))
103 mcs_secure_port
= kDefaultMCSMainSecurePort
;
106 } else if (!base::StringToInt(iter
->second
, &mcs_secure_port
)) {
107 DVLOG(1) << "Failed to parse MCS secure port: " << iter
->second
;
111 if (mcs_secure_port
< 0 || mcs_secure_port
> kMaxSecurePort
) {
112 DVLOG(1) << "Incorrect port value: " << mcs_secure_port
;
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();
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();
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
;
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
;
164 bool VerifySettings(const gcm::GServicesSettings::SettingsMap
& settings
) {
165 return VerifyCheckinInterval(settings
) && VerifyMCSEndpoint(settings
) &&
166 VerifyCheckinURL(settings
) && VerifyRegistrationURL(settings
);
174 const base::TimeDelta
GServicesSettings::MinimumCheckinInterval() {
175 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval
);
179 const GURL
GServicesSettings::DefaultCheckinURL() {
180 return GURL(kDefaultCheckinURL
);
184 std::string
GServicesSettings::CalculateDigest(const SettingsMap
& settings
) {
185 unsigned char hash
[base::kSHA1Length
];
187 for (SettingsMap::const_iterator iter
= settings
.begin();
188 iter
!= settings
.end();
192 data
+= iter
->second
;
196 reinterpret_cast<const unsigned char*>(&data
[0]), data
.size(), hash
);
198 kDigestVersionPrefix
+ base::HexEncode(hash
, base::kSHA1Length
);
199 digest
= base::StringToLowerASCII(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.";
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.
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();
226 DVLOG(1) << "Setting name is empty";
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
;
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
))
245 settings_
.swap(new_settings
);
246 digest_
= CalculateDigest(settings_
);
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())
255 if (!VerifySettings(load_result
.gservices_settings
))
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
;
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
;
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())
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
;
323 mcs_hostname
= kDefaultMCSHostname
;
325 // If constructed address makes sense use it.
327 MakeMCSEndpoint(mcs_hostname
, kDefaultMCSFallbackSecurePort
));
328 if (mcs_endpoint
.is_valid())
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
);