blob: 6e5908a3b8088829ec9bb657fc5b34a0c64d2e07 [file] [log] [blame]
// Copyright 2016 Google Inc. 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