| Index: google_apis/gcm/engine/gservices_settings.cc
|
| diff --git a/google_apis/gcm/engine/gservices_settings.cc b/google_apis/gcm/engine/gservices_settings.cc
|
| index 55cfa391392373f3326e0a8d34842a4095f1e3c7..4e989724b7e7333abd7e4cf53dd64e6ee841d62a 100644
|
| --- a/google_apis/gcm/engine/gservices_settings.cc
|
| +++ b/google_apis/gcm/engine/gservices_settings.cc
|
| @@ -5,7 +5,9 @@
|
| #include "google_apis/gcm/engine/gservices_settings.h"
|
|
|
| #include "base/bind.h"
|
| +#include "base/sha1.h"
|
| #include "base/strings/string_number_conversions.h"
|
| +#include "base/strings/string_util.h"
|
| #include "base/strings/stringprintf.h"
|
|
|
| namespace {
|
| @@ -28,12 +30,142 @@ const int kDefaultMCSMainSecurePort = 5228;
|
| const int kDefaultMCSFallbackSecurePort = 443;
|
| const char kDefaultRegistrationURL[] =
|
| "https://android.clients.google.com/c2dm/register3";
|
| +// Settings that are to be deleted are marked with this prefix in checkin
|
| +// response.
|
| +const char kDeleteSettingPrefix[] = "delete_";
|
| +// Settings digest starts with verison number followed by '-'.
|
| +const char kDigestVersionPrefix[] = "1-";
|
| const char kMCSEnpointTemplate[] = "https://%s:%d";
|
| +const int kMaxSecurePort = 65535;
|
|
|
| std::string MakeMCSEndpoint(const std::string& mcs_hostname, int port) {
|
| return base::StringPrintf(kMCSEnpointTemplate, mcs_hostname.c_str(), port);
|
| }
|
|
|
| +// Default settings can be omitted, as GServicesSettings class provides
|
| +// reasonable defaults.
|
| +bool CanBeOmitted(const std::string& settings_name) {
|
| + return settings_name == kCheckinIntervalKey ||
|
| + settings_name == kCheckinURLKey ||
|
| + settings_name == kMCSHostnameKey ||
|
| + settings_name == kMCSSecurePortKey ||
|
| + settings_name == kRegistrationURLKey;
|
| +}
|
| +
|
| +bool VerifyCheckinInterval(
|
| + const gcm::GServicesSettings::SettingsMap& settings) {
|
| + gcm::GServicesSettings::SettingsMap::const_iterator iter =
|
| + settings.find(kCheckinIntervalKey);
|
| + if (iter == settings.end())
|
| + return CanBeOmitted(kCheckinIntervalKey);
|
| +
|
| + int64 checkin_interval = kMinimumCheckinInterval;
|
| + if (!base::StringToInt64(iter->second, &checkin_interval)) {
|
| + DVLOG(1) << "Failed to parse checkin interval: " << iter->second;
|
| + return false;
|
| + }
|
| + if (checkin_interval == std::numeric_limits<int64>::max()) {
|
| + DVLOG(1) << "Checkin interval is too big: " << checkin_interval;
|
| + return false;
|
| + }
|
| + if (checkin_interval < kMinimumCheckinInterval) {
|
| + DVLOG(1) << "Checkin interval: " << checkin_interval
|
| + << " is less than allowed minimum: " << kMinimumCheckinInterval;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool VerifyMCSEndpoint(const gcm::GServicesSettings::SettingsMap& settings) {
|
| + std::string mcs_hostname;
|
| + gcm::GServicesSettings::SettingsMap::const_iterator iter =
|
| + settings.find(kMCSHostnameKey);
|
| + if (iter == settings.end()) {
|
| + // Because endpoint has 2 parts (hostname and port) we are defaulting and
|
| + // moving on with verification.
|
| + if (CanBeOmitted(kMCSHostnameKey))
|
| + mcs_hostname = kDefaultMCSHostname;
|
| + else
|
| + return false;
|
| + } else if (iter->second.empty()) {
|
| + DVLOG(1) << "Empty MCS hostname provided.";
|
| + return false;
|
| + } else {
|
| + mcs_hostname = iter->second;
|
| + }
|
| +
|
| + int mcs_secure_port = 0;
|
| + iter = settings.find(kMCSSecurePortKey);
|
| + if (iter == settings.end()) {
|
| + // Simlarly we might have to default the port, when only hostname is
|
| + // provided.
|
| + if (CanBeOmitted(kMCSSecurePortKey))
|
| + mcs_secure_port = kDefaultMCSMainSecurePort;
|
| + else
|
| + return false;
|
| + } else if (!base::StringToInt(iter->second, &mcs_secure_port)) {
|
| + DVLOG(1) << "Failed to parse MCS secure port: " << iter->second;
|
| + return false;
|
| + }
|
| +
|
| + if (mcs_secure_port < 0 || mcs_secure_port > kMaxSecurePort) {
|
| + DVLOG(1) << "Incorrect port value: " << mcs_secure_port;
|
| + return false;
|
| + }
|
| +
|
| + GURL mcs_main_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
|
| + if (!mcs_main_endpoint.is_valid()) {
|
| + DVLOG(1) << "Invalid main MCS endpoint: "
|
| + << mcs_main_endpoint.possibly_invalid_spec();
|
| + return false;
|
| + }
|
| + GURL mcs_fallback_endpoint(
|
| + MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
|
| + if (!mcs_fallback_endpoint.is_valid()) {
|
| + DVLOG(1) << "Invalid fallback MCS endpoint: "
|
| + << mcs_fallback_endpoint.possibly_invalid_spec();
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool VerifyCheckinURL(const gcm::GServicesSettings::SettingsMap& settings) {
|
| + gcm::GServicesSettings::SettingsMap::const_iterator iter =
|
| + settings.find(kCheckinURLKey);
|
| + if (iter == settings.end())
|
| + return CanBeOmitted(kCheckinURLKey);
|
| +
|
| + GURL checkin_url(iter->second);
|
| + if (!checkin_url.is_valid()) {
|
| + DVLOG(1) << "Invalid checkin URL provided: " << iter->second;
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool VerifyRegistrationURL(
|
| + const gcm::GServicesSettings::SettingsMap& settings) {
|
| + gcm::GServicesSettings::SettingsMap::const_iterator iter =
|
| + settings.find(kRegistrationURLKey);
|
| + if (iter == settings.end())
|
| + return CanBeOmitted(kRegistrationURLKey);
|
| +
|
| + GURL registration_url(iter->second);
|
| + if (!registration_url.is_valid()) {
|
| + DVLOG(1) << "Invalid registration URL provided: " << iter->second;
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool VerifySettings(const gcm::GServicesSettings::SettingsMap& settings) {
|
| + return VerifyCheckinInterval(settings) && VerifyMCSEndpoint(settings) &&
|
| + VerifyCheckinURL(settings) && VerifyRegistrationURL(settings);
|
| +}
|
| +
|
| } // namespace
|
|
|
| namespace gcm {
|
| @@ -48,152 +180,163 @@ const GURL GServicesSettings::DefaultCheckinURL() {
|
| return GURL(kDefaultCheckinURL);
|
| }
|
|
|
| -GServicesSettings::GServicesSettings()
|
| - : checkin_interval_(base::TimeDelta::FromSeconds(kDefaultCheckinInterval)),
|
| - checkin_url_(kDefaultCheckinURL),
|
| - mcs_main_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname,
|
| - kDefaultMCSMainSecurePort)),
|
| - mcs_fallback_endpoint_(MakeMCSEndpoint(kDefaultMCSHostname,
|
| - kDefaultMCSFallbackSecurePort)),
|
| - registration_url_(kDefaultRegistrationURL),
|
| - weak_ptr_factory_(this) {
|
| +// static
|
| +std::string GServicesSettings::CalculateDigest(const SettingsMap& settings) {
|
| + unsigned char hash[base::kSHA1Length];
|
| + std::string data;
|
| + for (SettingsMap::const_iterator iter = settings.begin();
|
| + iter != settings.end();
|
| + ++iter) {
|
| + data += iter->first;
|
| + data += '\0';
|
| + data += iter->second;
|
| + data += '\0';
|
| + }
|
| + base::SHA1HashBytes(
|
| + reinterpret_cast<const unsigned char*>(&data[0]), data.size(), hash);
|
| + std::string digest =
|
| + kDigestVersionPrefix + base::HexEncode(hash, base::kSHA1Length);
|
| + digest = StringToLowerASCII(digest);
|
| + return digest;
|
| +}
|
| +
|
| +GServicesSettings::GServicesSettings() : weak_ptr_factory_(this) {
|
| + digest_ = CalculateDigest(settings_);
|
| }
|
|
|
| -GServicesSettings::~GServicesSettings() {}
|
| +GServicesSettings::~GServicesSettings() {
|
| +}
|
|
|
| bool GServicesSettings::UpdateFromCheckinResponse(
|
| - const checkin_proto::AndroidCheckinResponse& checkin_response) {
|
| - if (!checkin_response.has_digest() ||
|
| - checkin_response.digest() == digest_) {
|
| - // There are no changes as digest is the same or no settings provided.
|
| + const checkin_proto::AndroidCheckinResponse& checkin_response) {
|
| + if (!checkin_response.has_settings_diff()) {
|
| + DVLOG(1) << "Field settings_diff not set in response.";
|
| return false;
|
| }
|
|
|
| - std::map<std::string, std::string> settings;
|
| + bool settings_diff = checkin_response.settings_diff();
|
| + SettingsMap new_settings;
|
| + // Only reuse the existing settings, if we are given a settings difference.
|
| + if (settings_diff)
|
| + new_settings = settings_map();
|
| +
|
| for (int i = 0; i < checkin_response.setting_size(); ++i) {
|
| std::string name = checkin_response.setting(i).name();
|
| - std::string value = checkin_response.setting(i).value();
|
| - settings[name] = value;
|
| - }
|
| + if (name.empty()) {
|
| + DVLOG(1) << "Setting name is empty";
|
| + return false;
|
| + }
|
|
|
| - // Only update the settings in store and digest, if the settings actually
|
| - // passed the verificaiton in update settings.
|
| - if (UpdateSettings(settings)) {
|
| - digest_ = checkin_response.digest();
|
| - return true;
|
| + if (settings_diff && name.find(kDeleteSettingPrefix) == 0) {
|
| + std::string setting_to_delete =
|
| + name.substr(arraysize(kDeleteSettingPrefix) - 1);
|
| + new_settings.erase(setting_to_delete);
|
| + DVLOG(1) << "Setting deleted: " << setting_to_delete;
|
| + } else {
|
| + std::string value = checkin_response.setting(i).value();
|
| + new_settings[name] = value;
|
| + DVLOG(1) << "New setting: '" << name << "' : '" << value << "'";
|
| + }
|
| }
|
|
|
| - return false;
|
| + if (!VerifySettings(new_settings))
|
| + return false;
|
| +
|
| + settings_.swap(new_settings);
|
| + digest_ = CalculateDigest(settings_);
|
| + return true;
|
| }
|
|
|
| void GServicesSettings::UpdateFromLoadResult(
|
| const GCMStore::LoadResult& load_result) {
|
| - if (UpdateSettings(load_result.gservices_settings))
|
| - digest_ = load_result.gservices_digest;
|
| -}
|
| + // No need to try to update settings when load_result is empty.
|
| + if (load_result.gservices_settings.empty())
|
| + return;
|
| + if (!VerifySettings(load_result.gservices_settings))
|
| + return;
|
| + std::string digest = CalculateDigest(load_result.gservices_settings);
|
| + if (digest != load_result.gservices_digest) {
|
| + DVLOG(1) << "G-services settings digest mismatch. "
|
| + << "Expected digest: " << load_result.gservices_digest
|
| + << ". Calculated digest is: " << digest;
|
| + return;
|
| + }
|
|
|
| -std::map<std::string, std::string> GServicesSettings::GetSettingsMap() const {
|
| - std::map<std::string, std::string> settings;
|
| - settings[kCheckinIntervalKey] =
|
| - base::Int64ToString(checkin_interval_.InSeconds());
|
| - settings[kCheckinURLKey] = checkin_url_.spec();
|
| - settings[kMCSHostnameKey] = mcs_main_endpoint_.host();
|
| - settings[kMCSSecurePortKey] = mcs_main_endpoint_.port();
|
| - settings[kRegistrationURLKey] = registration_url_.spec();
|
| - return settings;
|
| + settings_ = load_result.gservices_settings;
|
| + digest_ = load_result.gservices_digest;
|
| }
|
|
|
| -bool GServicesSettings::UpdateSettings(
|
| - const std::map<std::string, std::string>& settings) {
|
| - int64 new_checkin_interval = kMinimumCheckinInterval;
|
| - std::map<std::string, std::string>::const_iterator iter =
|
| - settings.find(kCheckinIntervalKey);
|
| - if (iter == settings.end()) {
|
| - LOG(ERROR) << "Setting not found: " << kCheckinIntervalKey;
|
| - return false;
|
| - }
|
| - if (!base::StringToInt64(iter->second, &new_checkin_interval)) {
|
| - LOG(ERROR) << "Failed to parse checkin interval: " << iter->second;
|
| - return false;
|
| - }
|
| - if (new_checkin_interval < kMinimumCheckinInterval) {
|
| - LOG(ERROR) << "Checkin interval: " << new_checkin_interval
|
| - << " is less than allowed minimum: " << kMinimumCheckinInterval;
|
| - new_checkin_interval = kMinimumCheckinInterval;
|
| - }
|
| - if (new_checkin_interval == std::numeric_limits<int64>::max()) {
|
| - LOG(ERROR) << "Checkin interval is too big: " << new_checkin_interval;
|
| - return false;
|
| +base::TimeDelta GServicesSettings::GetCheckinInterval() const {
|
| + int64 checkin_interval = kMinimumCheckinInterval;
|
| + SettingsMap::const_iterator iter = settings_.find(kCheckinIntervalKey);
|
| + if (iter == settings_.end() ||
|
| + !base::StringToInt64(iter->second, &checkin_interval)) {
|
| + checkin_interval = kDefaultCheckinInterval;
|
| }
|
|
|
| - std::string new_mcs_hostname;
|
| - iter = settings.find(kMCSHostnameKey);
|
| - if (iter == settings.end()) {
|
| - LOG(ERROR) << "Setting not found: " << kMCSHostnameKey;
|
| - return false;
|
| - }
|
| - new_mcs_hostname = iter->second;
|
| - if (new_mcs_hostname.empty()) {
|
| - LOG(ERROR) << "Empty MCS hostname provided.";
|
| - return false;
|
| - }
|
| + if (checkin_interval < kMinimumCheckinInterval)
|
| + checkin_interval = kMinimumCheckinInterval;
|
|
|
| - int new_mcs_secure_port = -1;
|
| - iter = settings.find(kMCSSecurePortKey);
|
| - if (iter == settings.end()) {
|
| - LOG(ERROR) << "Setting not found: " << kMCSSecurePortKey;
|
| - return false;
|
| - }
|
| - if (!base::StringToInt(iter->second, &new_mcs_secure_port)) {
|
| - LOG(ERROR) << "Failed to parse MCS secure port: " << iter->second;
|
| - return false;
|
| - }
|
| - if (new_mcs_secure_port < 0 || 65535 < new_mcs_secure_port) {
|
| - LOG(ERROR) << "Incorrect port value: " << new_mcs_secure_port;
|
| - return false;
|
| - }
|
| + return base::TimeDelta::FromSeconds(checkin_interval);
|
| +}
|
|
|
| - GURL new_mcs_main_endpoint =
|
| - GURL(MakeMCSEndpoint(new_mcs_hostname, new_mcs_secure_port));
|
| - GURL new_mcs_fallback_endpoint =
|
| - GURL(MakeMCSEndpoint(new_mcs_hostname, kDefaultMCSFallbackSecurePort));
|
| - if (!new_mcs_main_endpoint.is_valid() ||
|
| - !new_mcs_fallback_endpoint.is_valid())
|
| - return false;
|
| +GURL GServicesSettings::GetCheckinURL() const {
|
| + SettingsMap::const_iterator iter = settings_.find(kCheckinURLKey);
|
| + if (iter == settings_.end() || iter->second.empty())
|
| + return GURL(kDefaultCheckinURL);
|
| + return GURL(iter->second);
|
| +}
|
|
|
| - GURL new_checkin_url;
|
| - iter = settings.find(kCheckinURLKey);
|
| - if (iter == settings.end()) {
|
| - LOG(ERROR) << "Setting not found: " << kCheckinURLKey;
|
| - return false;
|
| - }
|
| - new_checkin_url = GURL(iter->second);
|
| - if (!new_checkin_url.is_valid()) {
|
| - LOG(ERROR) << "Invalid checkin URL provided: "
|
| - << new_checkin_url.possibly_invalid_spec();
|
| - return false;
|
| - }
|
| +GURL GServicesSettings::GetMCSMainEndpoint() const {
|
| + // Get alternative hostname or use default.
|
| + std::string mcs_hostname;
|
| + SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
|
| + if (iter != settings_.end() && !iter->second.empty())
|
| + mcs_hostname = iter->second;
|
| + else
|
| + mcs_hostname = kDefaultMCSHostname;
|
|
|
| - GURL new_registration_url;
|
| - iter = settings.find(kRegistrationURLKey);
|
| - if (iter == settings.end()) {
|
| - LOG(ERROR) << "Setting not found: " << kRegistrationURLKey;
|
| - return false;
|
| - }
|
| - new_registration_url = GURL(iter->second);
|
| - if (!new_registration_url.is_valid()) {
|
| - LOG(ERROR) << "Invalid registration URL provided: "
|
| - << new_registration_url.possibly_invalid_spec();
|
| - return false;
|
| + // Get alternative secure port or use defualt.
|
| + int mcs_secure_port = 0;
|
| + iter = settings_.find(kMCSSecurePortKey);
|
| + if (iter == settings_.end() || iter->second.empty() ||
|
| + !base::StringToInt(iter->second, &mcs_secure_port)) {
|
| + mcs_secure_port = kDefaultMCSMainSecurePort;
|
| }
|
|
|
| - // We only update the settings once all of them are correct.
|
| - checkin_interval_ = base::TimeDelta::FromSeconds(new_checkin_interval);
|
| - mcs_main_endpoint_ = new_mcs_main_endpoint;
|
| - mcs_fallback_endpoint_ = new_mcs_fallback_endpoint;
|
| - checkin_url_ = new_checkin_url;
|
| - registration_url_ = new_registration_url;
|
| - return true;
|
| + // If constructed address makes sense use it.
|
| + GURL mcs_endpoint(MakeMCSEndpoint(mcs_hostname, mcs_secure_port));
|
| + if (mcs_endpoint.is_valid())
|
| + return mcs_endpoint;
|
| +
|
| + // Otherwise use default settings.
|
| + return GURL(MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSMainSecurePort));
|
| +}
|
| +
|
| +GURL GServicesSettings::GetMCSFallbackEndpoint() const {
|
| + // Get alternative hostname or use default.
|
| + std::string mcs_hostname;
|
| + SettingsMap::const_iterator iter = settings_.find(kMCSHostnameKey);
|
| + if (iter != settings_.end() && !iter->second.empty())
|
| + mcs_hostname = iter->second;
|
| + else
|
| + mcs_hostname = kDefaultMCSHostname;
|
| +
|
| + // If constructed address makes sense use it.
|
| + GURL mcs_endpoint(
|
| + MakeMCSEndpoint(mcs_hostname, kDefaultMCSFallbackSecurePort));
|
| + if (mcs_endpoint.is_valid())
|
| + return mcs_endpoint;
|
| +
|
| + return GURL(
|
| + MakeMCSEndpoint(kDefaultMCSHostname, kDefaultMCSFallbackSecurePort));
|
| +}
|
| +
|
| +GURL GServicesSettings::GetRegistrationURL() const {
|
| + SettingsMap::const_iterator iter = settings_.find(kRegistrationURLKey);
|
| + if (iter == settings_.end() || iter->second.empty())
|
| + return GURL(kDefaultRegistrationURL);
|
| + return GURL(iter->second);
|
| }
|
|
|
| } // namespace gcm
|
|
|