| // Copyright 2016 The Cobalt Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "cobalt/storage/upgrade/upgrade_reader.h" | |
| #include <string> | |
| #include "base/i18n/time_formatting.h" | |
| #include "base/json/json_reader.h" | |
| #include "base/logging.h" | |
| #include "base/memory/scoped_ptr.h" | |
| #include "base/string_number_conversions.h" | |
| #include "googleurl/src/gurl.h" | |
| namespace cobalt { | |
| namespace storage { | |
| namespace upgrade { | |
| namespace { | |
| // The header at the start of upgrade data. | |
| const int kHeaderSize = 4; | |
| const char kHeader[] = "UPG0"; | |
| // Used as a sanity check. | |
| const int kMaxUpgradeDataSize = 10 * 1024 * 1024; | |
| // Deseralize a time value encoded as a possibly empty ASCII decimal string. | |
| base::Time StringToTime(const std::string& time_as_string, | |
| const base::Time& default_time) { | |
| if (!time_as_string.empty()) { | |
| int64 time_as_int64; | |
| if (base::StringToInt64(time_as_string, &time_as_int64)) { | |
| return base::Time::FromInternalValue(time_as_int64); | |
| } | |
| } | |
| return default_time; | |
| } | |
| base::Time GetSerializedTime(const base::DictionaryValue* dictionary, | |
| const std::string& field_name, | |
| const base::Time& default_time) { | |
| std::string time_string; | |
| dictionary->GetString(field_name, &time_string); | |
| return StringToTime(time_string, default_time); | |
| } | |
| // Get a list contained in a dictionary. | |
| const base::ListValue* GetList(const base::DictionaryValue* dictionary, | |
| const std::string& list_name) { | |
| if (!dictionary) { | |
| return NULL; | |
| } | |
| const base::ListValue* list = NULL; | |
| if (!dictionary->GetList(list_name, &list)) { | |
| return NULL; | |
| } | |
| return list; | |
| } | |
| // Get the size of a list contained in a dictionary. | |
| int GetListSize(const base::DictionaryValue* dictionary, | |
| const std::string& list_name) { | |
| const base::ListValue* list = GetList(dictionary, list_name); | |
| if (!list) { | |
| return 0; | |
| } | |
| return static_cast<int>(list->GetSize()); | |
| } | |
| // Get a dictionary in a list contained in a dictionary. | |
| const base::DictionaryValue* GetListItem( | |
| const base::DictionaryValue* dictionary, const std::string& list_name, | |
| int index) { | |
| const base::ListValue* list = GetList(dictionary, list_name); | |
| if (!list) { | |
| return NULL; | |
| } | |
| if (index < 0 || index >= static_cast<int>(list->GetSize())) { | |
| return NULL; | |
| } | |
| const base::DictionaryValue* list_item = NULL; | |
| if (!list->GetDictionary(static_cast<size_t>(index), &list_item)) { | |
| return NULL; | |
| } | |
| return list_item; | |
| } | |
| } // namespace | |
| void UpgradeReader::Parse(const char* data, int size) { | |
| DCHECK(data); | |
| DCHECK_GE(size, kHeaderSize); | |
| DCHECK_LE(size, kMaxUpgradeDataSize); | |
| // Check the header and offset the data. | |
| DCHECK(IsUpgradeData(data, size)); | |
| data += kHeaderSize; | |
| size -= kHeaderSize; | |
| base::JSONReader json_reader; | |
| scoped_ptr<base::Value> parsed( | |
| json_reader.ReadToValue(std::string(data, static_cast<size_t>(size)))); | |
| base::DictionaryValue* valid_dictionary = NULL; | |
| if (parsed) { | |
| parsed->GetAsDictionary(&valid_dictionary); | |
| } | |
| if (valid_dictionary) { | |
| ProcessValues(valid_dictionary); | |
| } else { | |
| DLOG(ERROR) << "Cannot parse upgrade save data: " | |
| << base::JSONReader::ErrorCodeToString( | |
| json_reader.error_code()); | |
| } | |
| } | |
| int UpgradeReader::GetNumCookies() const { | |
| return static_cast<int>(cookies_.size()); | |
| } | |
| int UpgradeReader::GetNumLocalStorageEntries() const { | |
| return static_cast<int>(local_storage_entries_.size()); | |
| } | |
| const net::CanonicalCookie* UpgradeReader::GetCookie(int index) const { | |
| if (index >= 0 && index < static_cast<int>(cookies_.size())) { | |
| return &cookies_[static_cast<size_t>(index)]; | |
| } else { | |
| return static_cast<const net::CanonicalCookie*>(NULL); | |
| } | |
| } | |
| void UpgradeReader::AddCookieIfValid(const base::DictionaryValue* cookie) { | |
| DCHECK(cookie); | |
| // Required attributes. | |
| std::string url; | |
| std::string name; | |
| std::string value; | |
| cookie->GetString("url", &url); | |
| cookie->GetString("name", &name); | |
| cookie->GetString("value", &value); | |
| if (url.empty() || name.empty() || value.empty()) { | |
| return; | |
| } | |
| // Optional attributes with default values. | |
| const GURL gurl(url); | |
| const std::string host = gurl.host(); | |
| std::string domain = host.empty() ? "" : host.substr(host.find(".")); | |
| cookie->GetString("domain", &domain); | |
| std::string path = "/"; | |
| cookie->GetString("path", &path); | |
| base::Time creation = | |
| GetSerializedTime(cookie, "creation", base::Time::Now()); | |
| base::Time expiration = | |
| GetSerializedTime(cookie, "expiration", base::Time()); | |
| base::Time last_access = | |
| GetSerializedTime(cookie, "last_access", base::Time::Now()); | |
| bool http_only = false; | |
| cookie->GetBoolean("http_only", &http_only); | |
| // Attributes not defined in upgrade data. | |
| const std::string mac_key; | |
| const std::string mac_algorithm; | |
| const bool secure = true; | |
| if (expiration.is_null()) { | |
| LOG(ERROR) << "\"" << name | |
| << "\" cookie can not be upgraded due to null expiration."; | |
| OnNullExpiration(); | |
| } | |
| LOG_IF(ERROR, expiration < base::Time::UnixEpoch()) | |
| << "\"" << name << "\" upgrade cookie expiration is " | |
| << base::TimeFormatFriendlyDateAndTime(expiration); | |
| cookies_.push_back(net::CanonicalCookie( | |
| gurl, name, value, domain, path, mac_key, mac_algorithm, creation, | |
| expiration, last_access, secure, http_only)); | |
| } | |
| const UpgradeReader::LocalStorageEntry* UpgradeReader::GetLocalStorageEntry( | |
| int index) const { | |
| if (index >= 0 && index < static_cast<int>(local_storage_entries_.size())) { | |
| return &local_storage_entries_[static_cast<size_t>(index)]; | |
| } else { | |
| return static_cast<const LocalStorageEntry*>(NULL); | |
| } | |
| } | |
| void UpgradeReader::AddLocalStorageEntryIfValid( | |
| const base::DictionaryValue* local_storage_entry) { | |
| DCHECK(local_storage_entry); | |
| std::string key; | |
| std::string value; | |
| if (!local_storage_entry->GetString("key", &key) || | |
| !local_storage_entry->GetString("value", &value) || key.empty() || | |
| value.empty()) { | |
| return; | |
| } | |
| local_storage_entries_.push_back(LocalStorageEntry(key, value)); | |
| } | |
| // static | |
| bool UpgradeReader::IsUpgradeData(const char* data, int size) { | |
| return size >= kHeaderSize && memcmp(data, kHeader, kHeaderSize) == 0; | |
| } | |
| void UpgradeReader::ProcessValues(const base::DictionaryValue* dictionary) { | |
| DCHECK(dictionary); | |
| const int num_cookies = GetListSize(dictionary, "cookies"); | |
| for (int n = 0; n < num_cookies; n++) { | |
| const base::DictionaryValue* cookie = GetListItem(dictionary, "cookies", n); | |
| if (cookie) { | |
| AddCookieIfValid(cookie); | |
| } | |
| } | |
| const int num_local_storage_entries = | |
| GetListSize(dictionary, "local_storage_entries"); | |
| for (int n = 0; n < num_local_storage_entries; n++) { | |
| const base::DictionaryValue* local_storage_entry = | |
| GetListItem(dictionary, "local_storage_entries", n); | |
| if (local_storage_entry) { | |
| AddLocalStorageEntryIfValid(local_storage_entry); | |
| } | |
| } | |
| } | |
| } // namespace upgrade | |
| } // namespace storage | |
| } // namespace cobalt |