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

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: Addressing CR feedback 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 "base/bind.h" 7 #include "base/bind.h"
8 #include "base/sha1.h"
8 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h" 11 #include "base/strings/stringprintf.h"
10 12
11 namespace { 13 namespace {
12 // The expected time in seconds between periodic checkins. 14 // The expected time in seconds between periodic checkins.
13 const char kCheckinIntervalKey[] = "checkin_interval"; 15 const char kCheckinIntervalKey[] = "checkin_interval";
14 // The override URL to the checkin server. 16 // The override URL to the checkin server.
15 const char kCheckinURLKey[] = "checkin_url"; 17 const char kCheckinURLKey[] = "checkin_url";
16 // The MCS machine name to connect to. 18 // The MCS machine name to connect to.
17 const char kMCSHostnameKey[] = "gcm_hostname"; 19 const char kMCSHostnameKey[] = "gcm_hostname";
18 // The MCS port to connect to. 20 // The MCS port to connect to.
19 const char kMCSSecurePortKey[] = "gcm_secure_port"; 21 const char kMCSSecurePortKey[] = "gcm_secure_port";
20 // The URL to get MCS registration IDs. 22 // The URL to get MCS registration IDs.
21 const char kRegistrationURLKey[] = "gcm_registration_url"; 23 const char kRegistrationURLKey[] = "gcm_registration_url";
22 24
23 const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days. 25 const int64 kDefaultCheckinInterval = 2 * 24 * 60 * 60; // seconds = 2 days.
24 const int64 kMinimumCheckinInterval = 12 * 60 * 60; // seconds = 12 hours. 26 const int64 kMinimumCheckinInterval = 12 * 60 * 60; // seconds = 12 hours.
25 const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin"; 27 const char kDefaultCheckinURL[] = "https://android.clients.google.com/checkin";
26 const char kDefaultMCSHostname[] = "mtalk.google.com"; 28 const char kDefaultMCSHostname[] = "mtalk.google.com";
27 const int kDefaultMCSMainSecurePort = 5228; 29 const int kDefaultMCSMainSecurePort = 5228;
28 const int kDefaultMCSFallbackSecurePort = 443; 30 const int kDefaultMCSFallbackSecurePort = 443;
29 const char kDefaultRegistrationURL[] = 31 const char kDefaultRegistrationURL[] =
30 "https://android.clients.google.com/c2dm/register3"; 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-";
31 const char kMCSEnpointTemplate[] = "https://%s:%d"; 38 const char kMCSEnpointTemplate[] = "https://%s:%d";
39 const int kMaxSecurePort = 65535;
32 40
33 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) { 41 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) {
34 return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port); 42 return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port);
35 } 43 }
36 44
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;
53 }
54
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);
61
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;
66 }
67 if (checkin_interval == std::numeric_limits<int64>::max()) {
68 DVLOG(1) << "Checkin interval is too big: " << checkin_interval;
69 return false;
70 }
71 if (checkin_interval < kMinimumCheckinInterval) {
72 DVLOG(1) << "Checkin interval: " << checkin_interval
73 << " is less than allowed minimum: " << kMinimumCheckinInterval;
74 }
75
76 return true;
77 }
78
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;
95 }
96
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;
109 }
110
111 if (mcs_secure_port < 0 || mcs_secure_port > kMaxSecurePort) {
112 DVLOG(1) << "Incorrect port value: " << mcs_secure_port;
113 return false;
114 }
115
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;
121 }
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;
128 }
129
130 return true;
131 }
132
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);
138
139 GURL checkin_url(iter->second);
140 if (!checkin_url.is_valid()) {
141 DVLOG(1) << "Invalid checkin URL provided: " << iter->second;
142 return false;
143 }
144
145 return true;
146 }
147
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);
154
155 GURL registration_url(iter->second);
156 if (!registration_url.is_valid()) {
157 DVLOG(1) << "Invalid registration URL provided: " << iter->second;
158 return false;
159 }
160
161 return true;
162 }
163
164 bool VerifySettings(const gcm::GServicesSettings::SettingsMap& settings) {
165 return VerifyCheckinInterval(settings) && VerifyMCSEndpoint(settings) &&
166 VerifyCheckinURL(settings) && VerifyRegistrationURL(settings);
167 }
168
37 } // namespace 169 } // namespace
38 170
39 namespace gcm { 171 namespace gcm {
40 172
41 // static 173 // static
42 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() { 174 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() {
43 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval); 175 return base::TimeDelta::FromSeconds(kMinimumCheckinInterval);
44 } 176 }
45 177
46 // static 178 // static
47 const GURL GServicesSettings::DefaultCheckinURL() { 179 const GURL GServicesSettings::DefaultCheckinURL() {
48 return GURL(kDefaultCheckinURL); 180 return GURL(kDefaultCheckinURL);
49 } 181 }
50 182
51 GServicesSettings::GServicesSettings() 183 // static
52 : checkin_interval_(base::TimeDelta::FromSeconds(kDefaultCheckinInterval)), 184 std::string GServicesSettings::CalculateDigest(const SettingsMap& settings) {
53 checkin_url_(kDefaultCheckinURL), 185 unsigned char hash[base::kSHA1Length];
54 mcs_main_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname, 186 std::string data;
55 kDefaultMCSMainSecurePort)), 187 for (SettingsMap::const_iterator iter = settings.begin();
56 mcs_fallback_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname, 188 iter != settings.end();
57 kDefaultMCSFallbackSecurePort)), 189 ++iter) {
58 registration_url_(kDefaultRegistrationURL), 190 data += iter->first;
59 weak_ptr_factory_(this) { 191 data += '\0';
192 data += iter->second;
193 data += '\0';
194 }
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;
60 } 201 }
61 202
62 GServicesSettings::~GServicesSettings() {} 203 GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
204 digest_ = CalculateDigest(settings_);
205 }
206
207 GServicesSettings::~GServicesSettings() {
208 }
63 209
64 bool GServicesSettings::UpdateFromCheckinResponse( 210 bool GServicesSettings::UpdateFromCheckinResponse(
65 const checkin_proto::AndroidCheckinResponse& checkin_response) { 211 const checkin_proto::AndroidCheckinResponse& checkin_response) {
66 if (!checkin_response.has_digest() || 212 if (!checkin_response.has_settings_diff()) {
67 checkin_response.digest() == digest_) { 213 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; 214 return false;
70 } 215 }
71 216
72 std::map<std::string, std::string> settings; 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();
222
73 for (int i = 0; i < checkin_response.setting_size(); ++i) { 223 for (int i = 0; i < checkin_response.setting_size(); ++i) {
74 std::string name = checkin_response.setting(i).name(); 224 std::string name = checkin_response.setting(i).name();
75 std::string value = checkin_response.setting(i).value(); 225 if (name.empty()) {
76 settings[name] = value; 226 DVLOG(1) << "Setting name is empty";
227 return false;
228 }
229
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 << "'";
239 }
77 } 240 }
78 241
79 // Only update the settings in store and digest, if the settings actually 242 if (!VerifySettings(new_settings))
80 // passed the verificaiton in update settings. 243 return false;
81 if (UpdateSettings(settings)) {
82 digest_ = checkin_response.digest();
83 return true;
84 }
85 244
86 return false; 245 settings_.swap(new_settings);
246 digest_ = CalculateDigest(settings_);
247 return true;
87 } 248 }
88 249
89 void GServicesSettings::UpdateFromLoadResult( 250 void GServicesSettings::UpdateFromLoadResult(
90 const GCMStore::LoadResult& load_result) { 251 const GCMStore::LoadResult& load_result) {
91 if (UpdateSettings(load_result.gservices_settings)) 252 // No need to try to update settings when load_result is empty.
92 digest_ = load_result.gservices_digest; 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;
263 }
264
265 settings_ = load_result.gservices_settings;
266 digest_ = load_result.gservices_digest;
93 } 267 }
94 268
95 std::map<std::string, std::string> GServicesSettings::GetSettingsMap() const { 269 base::TimeDelta GServicesSettings::GetCheckinInterval() const {
96 std::map<std::string, std::string> settings; 270 int64 checkin_interval = kMinimumCheckinInterval;
97 settings[kCheckinIntervalKey] = 271 SettingsMap::const_iterator iter = settings_.find(kCheckinIntervalKey);
98 base::Int64ToString(checkin_interval_.InSeconds()); 272 if (iter == settings_.end() ||
99 settings[kCheckinURLKey] = checkin_url_.spec(); 273 !base::StringToInt64(iter->second, &checkin_interval)) {
100 settings[kMCSHostnameKey] = mcs_main_endpoint_.host(); 274 checkin_interval = kDefaultCheckinInterval;
101 settings[kMCSSecurePortKey] = mcs_main_endpoint_.port(); 275 }
102 settings[kRegistrationURLKey] = registration_url_.spec(); 276
103 return settings; 277 if (checkin_interval < kMinimumCheckinInterval)
278 checkin_interval = kMinimumCheckinInterval;
279
280 return base::TimeDelta::FromSeconds(checkin_interval);
104 } 281 }
105 282
106 bool GServicesSettings::UpdateSettings( 283 GURL GServicesSettings::GetCheckinURL() const {
107 const std::map<std::string, std::string>& settings) { 284 SettingsMap::const_iterator iter = settings_.find(kCheckinURLKey);
108 int64 new_checkin_interval = kMinimumCheckinInterval; 285 if (iter == settings_.end() || iter->second.empty())
109 std::map<std::string, std::string>::const_iterator iter = 286 return GURL(kDefaultCheckinURL);
110 settings.find(kCheckinIntervalKey); 287 return GURL(iter->second);
111 if (iter == settings.end()) { 288 }
112 LOG(ERROR) << "Setting not found: " << kCheckinIntervalKey; 289
113 return false; 290 GURL GServicesSettings::GetMCSMainEndpoint() const {
114 } 291 // Get alternative hostname or use default.
115 if (!base::StringToInt64(iter->second, &new_checkin_interval)) { 292 std::string mcs_hostname;
116 LOG(ERROR) << "Failed to parse checkin interval: " << iter->second; 293 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
117 return false; 294 if (iter != settings_.end() && !iter->second.empty())
118 } 295 mcs_hostname = iter->second;
119 if (new_checkin_interval < kMinimumCheckinInterval) { 296 else
120 LOG(ERROR) << "Checkin interval: " << new_checkin_interval 297 mcs_hostname = kDefaultMCSHostname;
121 << " is less than allowed minimum: " << kMinimumCheckinInterval; 298
122 new_checkin_interval = kMinimumCheckinInterval; 299 // Get alternative secure port or use defualt.
123 } 300 int mcs_secure_port = 0;
124 if (new_checkin_interval == std::numeric_limits<int64>::max()) { 301 iter = settings_.find(kMCSSecurePortKey);
125 LOG(ERROR) << "Checkin interval is too big: " << new_checkin_interval; 302 if (iter == settings_.end() || iter->second.empty() ||
126 return false; 303 !base::StringToInt(iter->second, &mcs_secure_port)) {
304 mcs_secure_port = kDefaultMCSMainSecurePort;
127 } 305 }
128 306
129 std::string new_mcs_hostname; 307 // If constructed address makes sense use it.
130 iter = settings.find(kMCSHostnameKey); 308 GURL mcs_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
131 if (iter == settings.end()) { 309 if (mcs_endpoint.is_valid())
132 LOG(ERROR) << "Setting not found: " << kMCSHostnameKey; 310 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 311
141 int new_mcs_secure_port = -1; 312 // Otherwise use default settings.
142 iter = settings.find(kMCSSecurePortKey); 313 return GURL(MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSMainSecurePort));
143 if (iter == settings.end()) { 314 }
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 315
156 GURL new_mcs_main_endpoint = 316 GURL GServicesSettings::GetMCSFallbackEndpoint() const {
157 GURL(MakeMCSEndpoint(new_mcs_hostname, new_mcs_secure_port)); 317 // Get alternative hostname or use default.
158 GURL new_mcs_fallback_endpoint = 318 std::string mcs_hostname;
159 GURL(MakeMCSEndpoint(new_mcs_hostname, kDefaultMCSFallbackSecurePort)); 319 SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
160 if (!new_mcs_main_endpoint.is_valid() || 320 if (iter != settings_.end() && !iter->second.empty())
161 !new_mcs_fallback_endpoint.is_valid()) 321 mcs_hostname = iter->second;
162 return false; 322 else
323 mcs_hostname = kDefaultMCSHostname;
163 324
164 GURL new_checkin_url; 325 // If constructed address makes sense use it.
165 iter = settings.find(kCheckinURLKey); 326 GURL mcs_endpoint(
166 if (iter == settings.end()) { 327 MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
167 LOG(ERROR) << "Setting not found: " << kCheckinURLKey; 328 if (mcs_endpoint.is_valid())
168 return false; 329 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 330
177 GURL new_registration_url; 331 return GURL(
178 iter = settings.find(kRegistrationURLKey); 332 MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSFallbackSecurePort));
179 if (iter == settings.end()) { 333 }
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 334
190 // We only update the settings once all of them are correct. 335 GURL GServicesSettings::GetRegistrationURL() const {
191 checkin_interval_ = base::TimeDelta::FromSeconds(new_checkin_interval); 336 SettingsMap::const_iterator iter = settings_.find(kRegistrationURLKey);
192 mcs_main_endpoint_ = new_mcs_main_endpoint; 337 if (iter == settings_.end() || iter->second.empty())
193 mcs_fallback_endpoint_ = new_mcs_fallback_endpoint; 338 return GURL(kDefaultRegistrationURL);
194 checkin_url_ = new_checkin_url; 339 return GURL(iter->second);
195 registration_url_ = new_registration_url;
196 return true;
197 } 340 }
198 341
199 } // namespace gcm 342 } // 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