blob: 7c666c7f154059b93f23baba8f251753607a3db2 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file implements the platform-neutral Load() and Store() functions for a
// profile's safebrowsing.incidents_sent preference dictionary. The preference
// dict is converted to a protocol buffer message which is then serialized into
// a byte array. This serialized data is written to or read from some
// platform-specific storage via {Read,Write}StoreData implemented elsewhere.
//
// A pref dict like so:
// { "0": {"key1": "1235"}, {"key2": "6789"}}}
// is converted to an identical protocol buffer message, where the top-level
// mapping's keys are of type int, and the nested mappings' values are of type
// uint32_t.
#include "chrome/browser/safe_browsing/incident_reporting/platform_state_store.h"
#include "base/values.h"
#if defined(USE_PLATFORM_STATE_STORE)
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/safe_browsing/incident_reporting/state_store_data.pb.h"
#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
#endif // USE_PLATFORM_STATE_STORE
namespace safe_browsing {
namespace platform_state_store {
#if defined(USE_PLATFORM_STATE_STORE)
namespace {
using google::protobuf::RepeatedPtrField;
// Copies the (key, digest) pairs from |keys_and_digests| (a dict of string
// values) to the |key_digest_pairs| protobuf.
void KeysAndDigestsToProtobuf(
const base::DictionaryValue& keys_and_digests,
RepeatedPtrField<StateStoreData::Incidents::KeyDigestMapFieldEntry>*
key_digest_pairs) {
for (base::DictionaryValue::Iterator iter(keys_and_digests); !iter.IsAtEnd();
iter.Advance()) {
const base::StringValue* digest_value = nullptr;
if (!iter.value().GetAsString(&digest_value)) {
NOTREACHED();
continue;
}
uint32_t digest = 0;
if (!base::StringToUint(digest_value->GetString(), &digest)) {
NOTREACHED();
continue;
}
StateStoreData::Incidents::KeyDigestMapFieldEntry* key_digest =
key_digest_pairs->Add();
key_digest->set_key(iter.key());
key_digest->set_digest(digest);
}
}
// Copies the (type, dict) pairs from |incidents_sent| (a dict of dict values)
// to the |typed_incidents| protobuf.
void IncidentsSentToProtobuf(
const base::DictionaryValue& incidents_sent,
RepeatedPtrField<StateStoreData::TypeIncidentsMapFieldEntry>*
type_incidents_pairs) {
for (base::DictionaryValue::Iterator iter(incidents_sent); !iter.IsAtEnd();
iter.Advance()) {
const base::DictionaryValue* keys_and_digests = nullptr;
if (!iter.value().GetAsDictionary(&keys_and_digests)) {
NOTREACHED();
continue;
}
if (keys_and_digests->empty())
continue;
int incident_type = 0;
if (!base::StringToInt(iter.key(), &incident_type)) {
NOTREACHED();
continue;
}
StateStoreData::TypeIncidentsMapFieldEntry* entry =
type_incidents_pairs->Add();
entry->set_type(incident_type);
KeysAndDigestsToProtobuf(
*keys_and_digests, entry->mutable_incidents()->mutable_key_to_digest());
}
}
// Copies the (key, digest) pairs for a specific incident type into |type_dict|
// (a dict of string values).
void RestoreOfTypeFromProtobuf(
const RepeatedPtrField<StateStoreData::Incidents::KeyDigestMapFieldEntry>&
key_digest_pairs,
base::DictionaryValue* type_dict) {
for (const auto& key_digest : key_digest_pairs) {
if (!key_digest.has_key() || !key_digest.has_digest())
continue;
type_dict->SetStringWithoutPathExpansion(
key_digest.key(), base::UintToString(key_digest.digest()));
}
}
// Copies the (type, dict) pairs into |value_dict| (a dict of dict values).
void RestoreFromProtobuf(
const RepeatedPtrField<StateStoreData::TypeIncidentsMapFieldEntry>&
type_incidents_pairs,
base::DictionaryValue* value_dict) {
for (const auto& type_incidents : type_incidents_pairs) {
if (!type_incidents.has_type() || !type_incidents.has_incidents() ||
type_incidents.incidents().key_to_digest_size() == 0) {
continue;
}
std::string type_string(base::IntToString(type_incidents.type()));
base::DictionaryValue* type_dict = nullptr;
if (!value_dict->GetDictionaryWithoutPathExpansion(type_string,
&type_dict)) {
type_dict = new base::DictionaryValue();
value_dict->SetWithoutPathExpansion(type_string, type_dict);
}
RestoreOfTypeFromProtobuf(type_incidents.incidents().key_to_digest(),
type_dict);
}
}
} // namespace
#endif // USE_PLATFORM_STATE_STORE
scoped_ptr<base::DictionaryValue> Load(Profile* profile) {
#if defined(USE_PLATFORM_STATE_STORE)
scoped_ptr<base::DictionaryValue> value_dict(new base::DictionaryValue());
std::string data;
PlatformStateStoreLoadResult result = ReadStoreData(profile, &data);
if (result == PlatformStateStoreLoadResult::SUCCESS)
result = DeserializeIncidentsSent(data, value_dict.get());
switch (result) {
case PlatformStateStoreLoadResult::SUCCESS:
case PlatformStateStoreLoadResult::CLEARED_DATA:
case PlatformStateStoreLoadResult::CLEARED_NO_DATA:
// Return a (possibly empty) dictionary for the success cases.
break;
case PlatformStateStoreLoadResult::DATA_CLEAR_FAILED:
case PlatformStateStoreLoadResult::OPEN_FAILED:
case PlatformStateStoreLoadResult::READ_FAILED:
case PlatformStateStoreLoadResult::PARSE_ERROR:
// Return null for all error cases.
value_dict.reset();
break;
case PlatformStateStoreLoadResult::NUM_RESULTS:
NOTREACHED();
break;
}
UMA_HISTOGRAM_ENUMERATION(
"SBIRS.PSSLoadResult", static_cast<uint32_t>(result),
static_cast<uint32_t>(PlatformStateStoreLoadResult::NUM_RESULTS));
return value_dict.Pass();
#else
return scoped_ptr<base::DictionaryValue>();
#endif
}
void Store(Profile* profile, const base::DictionaryValue* incidents_sent) {
#if defined(USE_PLATFORM_STATE_STORE)
std::string data;
SerializeIncidentsSent(incidents_sent, &data);
UMA_HISTOGRAM_COUNTS("SBIRS.PSSDataStoreSize", data.size());
WriteStoreData(profile, data);
#endif
}
#if defined(USE_PLATFORM_STATE_STORE)
void SerializeIncidentsSent(const base::DictionaryValue* incidents_sent,
std::string* data) {
StateStoreData store_data;
IncidentsSentToProtobuf(*incidents_sent,
store_data.mutable_type_to_incidents());
store_data.SerializeToString(data);
}
PlatformStateStoreLoadResult DeserializeIncidentsSent(
const std::string& data,
base::DictionaryValue* value_dict) {
StateStoreData store_data;
if (data.empty()) {
value_dict->Clear();
return PlatformStateStoreLoadResult::SUCCESS;
}
if (!store_data.ParseFromString(data))
return PlatformStateStoreLoadResult::PARSE_ERROR;
value_dict->Clear();
RestoreFromProtobuf(store_data.type_to_incidents(), value_dict);
return PlatformStateStoreLoadResult::SUCCESS;
}
#endif // USE_PLATFORM_STATE_STORE
} // namespace platform_state_store
} // namespace safe_browsing