Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / google_apis / gcm / engine / gservices_settings.cc
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.
4
5 #include "google_apis/gcm/engine/gservices_settings.h"
6
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"
12
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";
24
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;
40
41 std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) {
42   return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port);
43 }
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
169 }  // namespace
170
171 namespace gcm {
172
173 // static
174 const base::TimeDelta GServicesSettings::MinimumCheckinInterval() {
175   return base::TimeDelta::FromSeconds(kMinimumCheckinInterval);
176 }
177
178 // static
179 const GURL GServicesSettings::DefaultCheckinURL() {
180   return GURL(kDefaultCheckinURL);
181 }
182
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';
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 = base::StringToLowerASCII(digest);
200   return digest;
201 }
202
203 GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
204   digest_ = CalculateDigest(settings_);
205 }
206
207 GServicesSettings::~GServicesSettings() {
208 }
209
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;
215   }
216
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
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;
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     }
240   }
241
242   if (!VerifySettings(new_settings))
243     return false;
244
245   settings_.swap(new_settings);
246   digest_ = CalculateDigest(settings_);
247   return true;
248 }
249
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;
263   }
264
265   settings_ = load_result.gservices_settings;
266   digest_ = load_result.gservices_digest;
267 }
268
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;
275   }
276
277   if (checkin_interval < kMinimumCheckinInterval)
278     checkin_interval = kMinimumCheckinInterval;
279
280   return base::TimeDelta::FromSeconds(checkin_interval);
281 }
282
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);
288 }
289
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;
298
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;
305   }
306
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;
311
312   // Otherwise use default settings.
313   return GURL(MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSMainSecurePort));
314 }
315
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;
324
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;
330
331   return GURL(
332       MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSFallbackSecurePort));
333 }
334
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);
340 }
341
342 }  // namespace gcm