Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(648)

Side by Side Diff: google_apis/gcm/engine/gservices_settings.cc

Issue 288433002: G-services settings v3 implementation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixing V3 protocol implementation and updating tests. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "google_apis/gcm/engine/gservices_settings.h" 5 #include "google_apis/gcm/engine/gservices_settings.h"
6 6
7 #include <algorithm>
8
7 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/sha1.h"
8 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/stringprintf.h" 12 #include "base/strings/stringprintf.h"
10 13
11 namespace { 14 namespace {
12 // The expected time in seconds between periodic checkins. 15 // The expected time in seconds between periodic checkins.
13 const char kCheckinIntervalKey[] = "checkin_interval"; 16 const char kCheckinIntervalKey[] = "checkin_interval";
14 // The override URL to the checkin server. 17 // The override URL to the checkin server.
15 const char kCheckinURLKey[] = "checkin_url"; 18 const char kCheckinURLKey[] = "checkin_url";
16 // The MCS machine name to connect to. 19 // The MCS machine name to connect to.
17 const char kMCSHostnameKey[] = "gcm_hostname"; 20 const char kMCSHostnameKey[] = "gcm_hostname";
18 // The MCS port to connect to. 21 // The MCS port to connect to.
19 const char kMCSSecurePortKey[] = "gcm_secure_port"; 22 const char kMCSSecurePortKey[] = "gcm_secure_port";
20 // The URL to get MCS registration IDs. 23 // The URL to get MCS registration IDs.
21 const char kRegistrationURLKey[] = "gcm_registration_url"; 24 const char kRegistrationURLKey[] = "gcm_registration_url";
22 25
23 const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days. 26 const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days.
24 const int64 kMinimumCheckinInterval = 12 * 60 * 60; // seconds = 12 hours. 27 const int64 kMinimumCheckinInterval = 12 * 60 * 60; // seconds = 12 hours.
25 const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin"; 28 const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin";
26 const char kDefaultMCSHostname[] = "mtalk.google.com"; 29 const char kDefaultMCSHostname[] = "mtalk.google.com";
27 const int kDefaultMCSMainSecurePort = 5228; 30 const int kDefaultMCSMainSecurePort = 5228;
28 const int kDefaultMCSFallbackSecurePort = 443; 31 const int kDefaultMCSFallbackSecurePort = 443;
29 const char kDefaultRegistrationURL[] = 32 const char kDefaultRegistrationURL[] =
30 "https://android.clients.google.com/c2dm/register3"; 33 "https://android.clients.google.com/c2dm/register3";
34 // Settings that are to be deleted are marked with this prefix in checkin
35 // response.
36 const char kDeleteSettingPrefix[] = "delete_";
37 // Settings digest starts with verison number followed by '-'.
38 const char kDigestVersionPrefix[] = "1-";
31 const char kMCSEnpointTemplate[] = "https://%s:%d"; 39 const char kMCSEnpointTemplate[] = "https://%s:%d";
40 const int kMaxSecurePort = 65535;
32 41
33 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) { 42 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) {
34 return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port); 43 return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port);
35 } 44 }
36 45
46 bool VerifyCheckinInterval(
47 const gcm::GServicesSettings::SettingsMap& settings) {
48 gcm::GServicesSettings::SettingsMap::const_iterator iter =
49 settings.find(kCheckinIntervalKey);
50 // Default settings are only present in the map when overwritten.
jianli 2014/05/19 17:52:27 Comment is a bit hard to understand. Do you mean:
fgorski 2014/05/19 20:25:39 Done.
51 if (iter == settings.end())
Nicolas Zea 2014/05/19 17:49:48 It would be useful to have some helper method that
fgorski 2014/05/19 20:25:39 I added CanBeOmitted method with a documentation.
52 return true;
53
54 int64 checkin_interval = kMinimumCheckinInterval;
55 if (!base::StringToInt64(iter->second, &checkin_interval)) {
56 DVLOG(1) << "Failed to parse checkin interval: " << iter->second;
57 return false;
58 }
59 if (checkin_interval == std::numeric_limits<int64>::max()) {
60 DVLOG(1) << "Checkin interval is too big: " << checkin_interval;
61 return false;
62 }
63 if (checkin_interval < kMinimumCheckinInterval) {
64 DVLOG(1) << "Checkin interval: " << checkin_interval
65 << " is less than allowed minimum: " << kMinimumCheckinInterval;
66 }
67
68 return true;
69 }
70
71 bool VerifyMCSEndpoint(const gcm::GServicesSettings::SettingsMap& settings) {
72 // Default settings are only present in the map when overwritten.
73 std::string mcs_hostname = kDefaultMCSHostname;
74 gcm::GServicesSettings::SettingsMap::const_iterator iter =
75 settings.find(kMCSHostnameKey);
76 if (iter != settings.end())
jianli 2014/05/19 17:52:27 This seems to be consistent with other Verify meth
fgorski 2014/05/19 20:25:39 I've updated it to be more consistent, but it is a
77 mcs_hostname = iter->second;
78
79 if (mcs_hostname.empty()) {
80 DVLOG(1) << "Empty MCS hostname provided.";
81 return false;
82 }
83
84 int mcs_secure_port = 0;
85 iter = settings.find(kMCSSecurePortKey);
86 if (iter == settings.end()) {
87 mcs_secure_port = kDefaultMCSMainSecurePort;
88 } else if (!base::StringToInt(iter->second, &mcs_secure_port)) {
89 DVLOG(1) << "Failed to parse MCS secure port: " << iter->second;
90 return false;
91 }
92
93 if (mcs_secure_port < 0 || mcs_secure_port > kMaxSecurePort) {
94 DVLOG(1) << "Incorrect port value: " << mcs_secure_port;
95 return false;
96 }
97
98 GURL mcs_main_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
99 if (!mcs_main_endpoint.is_valid()) {
100 DVLOG(1) << "Invalid main MCS endpoint: "
101 << mcs_main_endpoint.possibly_invalid_spec();
102 return false;
103 }
104 GURL mcs_fallback_endpoint(
105 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
106 if (!mcs_fallback_endpoint.is_valid()) {
107 DVLOG(1) << "Invalid fallback MCS endpoint: "
108 << mcs_fallback_endpoint.possibly_invalid_spec();
109 return false;
110 }
111
112 return true;
113 }
114
115 bool VerifyCheckinURL(const gcm::GServicesSettings::SettingsMap& settings) {
116 gcm::GServicesSettings::SettingsMap::const_iterator iter =
117 settings.find(kCheckinURLKey);
118 // Default settings are only present in the map when overwritten.
119 if (iter == settings.end())
120 return true;
121
122 GURL checkin_url(iter->second);
123 if (!checkin_url.is_valid()) {
124 DVLOG(1) << "Invalid checkin URL provided: " << iter->second;
125 return false;
126 }
127
128 return true;
129 }
130
131 bool VerifyRegistrationURL(
132 const gcm::GServicesSettings::SettingsMap& settings) {
133 gcm::GServicesSettings::SettingsMap::const_iterator iter =
134 settings.find(kRegistrationURLKey);
135 // Default settings are only present in the map when overwritten.
136 if (iter == settings.end())
137 return true;
138
139 GURL registration_url(iter->second);
140 if (!registration_url.is_valid()) {
141 DVLOG(1) << "Invalid registration URL provided: " << iter->second;
142 return false;
143 }
144
145 return true;
146 }
147
148 bool VerifySettings(const gcm::GServicesSettings::SettingsMap& settings) {
149 return VerifyCheckinInterval(settings) && VerifyMCSEndpoint(settings) &&
150 VerifyCheckinURL(settings) && VerifyRegistrationURL(settings);
151 }
152
37 } // namespace 153 } // namespace
38 154
39 namespace gcm { 155 namespace gcm {
40 156
41 // static 157 // static
42 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() { 158 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() {
43 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval); 159 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval);
44 } 160 }
45 161
46 // static 162 // static
47 const GURL GServicesSettings::DefaultCheckinURL() { 163 const GURL GServicesSettings::DefaultCheckinURL() {
48 return GURL(kDefaultCheckinURL); 164 return GURL(kDefaultCheckinURL);
49 } 165 }
50 166
51 GServicesSettings::GServicesSettings() 167 // static
52 : checkin_interval_(base::TimeDelta::FromSeconds(kDefaultCheckinInterval)), 168 std::string GServicesSettings::CalculateDigest(const SettingsMap& settings) {
53 checkin_url_(kDefaultCheckinURL), 169 unsigned char hash[base::kSHA1Length];
54 mcs_main_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname, 170 std::string data;
55 kDefaultMCSMainSecurePort)), 171 for (SettingsMap::const_iterator iter = settings.begin();
56 mcs_fallback_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname, 172 iter != settings.end();
57 kDefaultMCSFallbackSecurePort)), 173 ++iter) {
58 registration_url_(kDefaultRegistrationURL), 174 data += iter->first;
59 weak_ptr_factory_(this) { 175 data += '\0';
176 data += iter->second;
177 data += '\0';
178 }
179 base::SHA1HashBytes(
180 reinterpret_cast<const unsigned char*>(&data[0]), data.size(), hash);
181 std::string digest =
182 kDigestVersionPrefix + base::HexEncode(hash, base::kSHA1Length);
183 std::transform(digest.begin(), digest.end(), digest.begin(), ::tolower);
jianli 2014/05/19 17:52:27 nit: use StringToLowerASCII instead.
fgorski 2014/05/19 20:25:39 Done.
184 return digest;
60 } 185 }
61 186
62 GServicesSettings::~GServicesSettings() {} 187 GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
188 digest_ = CalculateDigest(settings_);
189 }
190
191 GServicesSettings::~GServicesSettings() {
192 }
63 193
64 bool GServicesSettings::UpdateFromCheckinResponse( 194 bool GServicesSettings::UpdateFromCheckinResponse(
65 const checkin_proto::AndroidCheckinResponse& checkin_response) { 195 const checkin_proto::AndroidCheckinResponse& checkin_response) {
66 if (!checkin_response.has_digest() || 196 if (!checkin_response.has_settings_diff()) {
67 checkin_response.digest() == digest_) { 197 DVLOG(1) << "Field settings_diff not set in response.";
68 // There are no changes as digest is the same or no settings provided.
69 return false; 198 return false;
70 } 199 }
71 200
72 std::map<std::string, std::string> settings; 201 bool settings_diff = checkin_response.settings_diff();
202 SettingsMap new_settings;
203 // Only reuse the existing settings, if we are given a settings difference.
204 if (settings_diff)
205 new_settings = settings_map();
206
73 for (int i = 0; i < checkin_response.setting_size(); ++i) { 207 for (int i = 0; i < checkin_response.setting_size(); ++i) {
74 std::string name = checkin_response.setting(i).name(); 208 std::string name = checkin_response.setting(i).name();
75 std::string value = checkin_response.setting(i).value(); 209 if (name.empty()) {
76 settings[name] = value; 210 DVLOG(1) << "Setting name is empty";
211 return false;
212 }
213
214 if (settings_diff && name.find(kDeleteSettingPrefix) == 0) {
215 std::string setting_to_delete =
216 name.substr(arraysize(kDeleteSettingPrefix) - 1);
217 new_settings.erase(setting_to_delete);
218 DVLOG(1) << "Setting deleted: " << setting_to_delete;
219 } else {
220 std::string value = checkin_response.setting(i).value();
221 new_settings[name] = value;
222 DVLOG(1) << "New setting: '" << name << "' : '" << value << "'";
223 }
77 } 224 }
78 225
79 // Only update the settings in store and digest, if the settings actually 226 if (!VerifySettings(new_settings))
80 // passed the verificaiton in update settings. 227 return false;
81 if (UpdateSettings(settings)) {
82 digest_ = checkin_response.digest();
83 return true;
84 }
85 228
86 return false; 229 settings_.swap(new_settings);
230 digest_ = CalculateDigest(settings_);
231 return true;
87 } 232 }
88 233
89 void GServicesSettings::UpdateFromLoadResult( 234 void GServicesSettings::UpdateFromLoadResult(
90 const GCMStore::LoadResult& load_result) { 235 const GCMStore::LoadResult& load_result) {
91 if (UpdateSettings(load_result.gservices_settings)) 236 // No need to try to update settings when load_result is empty.
92 digest_ = load_result.gservices_digest; 237 if (load_result.gservices_settings.empty())
238 return;
239 if (!VerifySettings(load_result.gservices_settings))
240 return;
241 std::string digest = CalculateDigest(load_result.gservices_settings);
242 if (digest != load_result.gservices_digest) {
243 DVLOG(1) << "G-services settings digest mismatch. "
244 << "Expected digest: " << load_result.gservices_digest
245 << ". Calculated digest is: " << digest;
246 return;
247 }
248
249 settings_ = load_result.gservices_settings;
250 digest_ = load_result.gservices_digest;
93 } 251 }
94 252
95 std::map<std::string, std::string> GServicesSettings::GetSettingsMap() const { 253 base::TimeDelta GServicesSettings::GetCheckinInterval() const {
96 std::map<std::string, std::string> settings; 254 int64 checkin_interval = kMinimumCheckinInterval;
97 settings[kCheckinIntervalKey] = 255 SettingsMap::const_iterator iter = settings_.find(kCheckinIntervalKey);
98 base::Int64ToString(checkin_interval_.InSeconds()); 256 if (iter == settings_.end() ||
99 settings[kCheckinURLKey] = checkin_url_.spec(); 257 !base::StringToInt64(iter->second, &checkin_interval)) {
100 settings[kMCSHostnameKey] = mcs_main_endpoint_.host(); 258 checkin_interval = kDefaultCheckinInterval;
101 settings[kMCSSecurePortKey] = mcs_main_endpoint_.port(); 259 }
102 settings[kRegistrationURLKey] = registration_url_.spec(); 260
103 return settings; 261 if (checkin_interval < kMinimumCheckinInterval)
262 checkin_interval = kMinimumCheckinInterval;
263
264 return base::TimeDelta::FromSeconds(checkin_interval);
104 } 265 }
105 266
106 bool GServicesSettings::UpdateSettings( 267 GURL GServicesSettings::GetCheckinURL() const {
107 const std::map<std::string, std::string>& settings) { 268 SettingsMap::const_iterator iter = settings_.find(kCheckinURLKey);
108 int64 new_checkin_interval = kMinimumCheckinInterval; 269 if (iter == settings_.end() || iter->second.empty())
109 std::map<std::string, std::string>::const_iterator iter = 270 return GURL(kDefaultCheckinURL);
110 settings.find(kCheckinIntervalKey); 271 return GURL(iter->second);
111 if (iter == settings.end()) { 272 }
112 LOG(ERROR) << "Setting not found: " << kCheckinIntervalKey; 273
113 return false; 274 GURL GServicesSettings::GetMCSMainEndpoint() const {
114 } 275 // Get alternative hostname or use default.
115 if (!base::StringToInt64(iter->second, &new_checkin_interval)) { 276 std::string mcs_hostname(kDefaultMCSHostname);
jianli 2014/05/19 17:52:27 I think it will be more efficient not to set the d
fgorski 2014/05/19 20:25:39 Done.
116 LOG(ERROR) << "Failed to parse checkin interval: " << iter->second; 277 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
117 return false; 278 if (iter != settings_.end() && !iter->second.empty())
118 } 279 mcs_hostname = iter->second;
119 if (new_checkin_interval < kMinimumCheckinInterval) { 280
120 LOG(ERROR) << "Checkin interval: " << new_checkin_interval 281 // Get alternative secure port or use defualt.
121 << " is less than allowed minimum: " << kMinimumCheckinInterval; 282 int mcs_secure_port = 0;
122 new_checkin_interval = kMinimumCheckinInterval; 283 iter = settings_.find(kMCSSecurePortKey);
123 } 284 if (iter == settings_.end() || iter->second.empty() ||
124 if (new_checkin_interval == std::numeric_limits<int64>::max()) { 285 !base::StringToInt(iter->second, &mcs_secure_port)) {
125 LOG(ERROR) << "Checkin interval is too big: " << new_checkin_interval; 286 mcs_secure_port = kDefaultMCSMainSecurePort;
126 return false;
127 } 287 }
128 288
129 std::string new_mcs_hostname; 289 // If constructed address makes sense use it.
130 iter = settings.find(kMCSHostnameKey); 290 GURL mcs_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
131 if (iter == settings.end()) { 291 if (mcs_endpoint.is_valid())
132 LOG(ERROR) << "Setting not found: " << kMCSHostnameKey; 292 return mcs_endpoint;
133 return false;
134 }
135 new_mcs_hostname = iter->second;
136 if (new_mcs_hostname.empty()) {
137 LOG(ERROR) << "Empty MCS hostname provided.";
138 return false;
139 }
140 293
141 int new_mcs_secure_port = -1; 294 // Otherwise use default settings.
142 iter = settings.find(kMCSSecurePortKey); 295 return GURL(MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSMainSecurePort));
143 if (iter == settings.end()) { 296 }
144 LOG(ERROR) << "Setting not found: " << kMCSSecurePortKey;
145 return false;
146 }
147 if (!base::StringToInt(iter->second, &new_mcs_secure_port)) {
148 LOG(ERROR) << "Failed to parse MCS secure port: " << iter->second;
149 return false;
150 }
151 if (new_mcs_secure_port < 0 || 65535 < new_mcs_secure_port) {
152 LOG(ERROR) << "Incorrect port value: " << new_mcs_secure_port;
153 return false;
154 }
155 297
156 GURL new_mcs_main_endpoint = 298 GURL GServicesSettings::GetMCSFallbackEndpoint() const {
157 GURL(MakeMCSEndpoint(new_mcs_hostname, new_mcs_secure_port)); 299 // Get alternative hostname or use default.
158 GURL new_mcs_fallback_endpoint = 300 std::string mcs_hostname(kDefaultMCSHostname);
jianli 2014/05/19 17:52:27 ditto
fgorski 2014/05/19 20:25:39 Done.
159 GURL(MakeMCSEndpoint(new_mcs_hostname, kDefaultMCSFallbackSecurePort)); 301 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
160 if (!new_mcs_main_endpoint.is_valid() || 302 if (iter != settings_.end() && !iter->second.empty())
161 !new_mcs_fallback_endpoint.is_valid()) 303 mcs_hostname = iter->second;
162 return false;
163 304
164 GURL new_checkin_url; 305 // If constructed address makes sense use it.
165 iter = settings.find(kCheckinURLKey); 306 GURL mcs_endpoint(
166 if (iter == settings.end()) { 307 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
167 LOG(ERROR) << "Setting not found: " << kCheckinURLKey; 308 if (mcs_endpoint.is_valid())
168 return false; 309 return mcs_endpoint;
169 }
170 new_checkin_url = GURL(iter->second);
171 if (!new_checkin_url.is_valid()) {
172 LOG(ERROR) << "Invalid checkin URL provided: "
173 << new_checkin_url.possibly_invalid_spec();
174 return false;
175 }
176 310
177 GURL new_registration_url; 311 return GURL(
178 iter = settings.find(kRegistrationURLKey); 312 MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSFallbackSecurePort));
179 if (iter == settings.end()) { 313 }
180 LOG(ERROR) << "Setting not found: " << kRegistrationURLKey;
181 return false;
182 }
183 new_registration_url = GURL(iter->second);
184 if (!new_registration_url.is_valid()) {
185 LOG(ERROR) << "Invalid registration URL provided: "
186 << new_registration_url.possibly_invalid_spec();
187 return false;
188 }
189 314
190 // We only update the settings once all of them are correct. 315 GURL GServicesSettings::GetRegistrationURL() const {
191 checkin_interval_ = base::TimeDelta::FromSeconds(new_checkin_interval); 316 SettingsMap::const_iterator iter = settings_.find(kRegistrationURLKey);
192 mcs_main_endpoint_ = new_mcs_main_endpoint; 317 if (iter == settings_.end() || iter->second.empty())
193 mcs_fallback_endpoint_ = new_mcs_fallback_endpoint; 318 return GURL(kDefaultRegistrationURL);
194 checkin_url_ = new_checkin_url; 319 return GURL(iter->second);
195 registration_url_ = new_registration_url;
196 return true;
197 } 320 }
198 321
199 } // namespace gcm 322 } // namespace gcm
OLDNEW
« no previous file with comments | « google_apis/gcm/engine/gservices_settings.h ('k') | google_apis/gcm/engine/gservices_settings_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698