blob: 3e06dbaa5e28f11025f77ba3819839e0c5ead99b [file] [log] [blame]
// Copyright 2018 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.
#include "extensions/browser/json_file_sanitizer.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/json/json_string_value_serializer.h"
#include "base/task_runner_util.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "services/service_manager/public/cpp/connector.h"
namespace extensions {
namespace {
// Reads the file in |path| and then deletes it.
// Returns a tuple containing: the file content, whether the read was
// successful, whether the delete was successful.
std::tuple<std::string, bool, bool> ReadAndDeleteTextFile(
const base::FilePath& path) {
std::string contents;
bool read_success = base::ReadFileToString(path, &contents);
bool delete_success = base::DeleteFile(path, /*recursive=*/false);
return std::make_tuple(contents, read_success, delete_success);
}
int WriteStringToFile(const std::string& contents,
const base::FilePath& file_path) {
int size = static_cast<int>(contents.length());
return base::WriteFile(file_path, contents.data(), size);
}
} // namespace
// static
std::unique_ptr<JsonFileSanitizer> JsonFileSanitizer::CreateAndStart(
service_manager::Connector* connector,
const service_manager::ServiceFilter& service_filter,
const std::set<base::FilePath>& file_paths,
Callback callback) {
// Note we can't use std::make_unique as we want to keep the constructor
// private.
std::unique_ptr<JsonFileSanitizer> sanitizer(
new JsonFileSanitizer(file_paths, std::move(callback)));
sanitizer->Start(connector, service_filter);
return sanitizer;
}
JsonFileSanitizer::JsonFileSanitizer(const std::set<base::FilePath>& file_paths,
Callback callback)
: file_paths_(file_paths),
callback_(std::move(callback)),
weak_factory_(this) {}
JsonFileSanitizer::~JsonFileSanitizer() = default;
void JsonFileSanitizer::Start(
service_manager::Connector* connector,
const service_manager::ServiceFilter& service_filter) {
if (file_paths_.empty()) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&JsonFileSanitizer::ReportSuccess,
weak_factory_.GetWeakPtr()));
return;
}
connector->BindInterface(service_filter, &json_parser_ptr_);
for (const base::FilePath& path : file_paths_) {
base::PostTaskAndReplyWithResult(
extensions::GetExtensionFileTaskRunner().get(), FROM_HERE,
base::BindOnce(&ReadAndDeleteTextFile, path),
base::BindOnce(&JsonFileSanitizer::JsonFileRead,
weak_factory_.GetWeakPtr(), path));
}
}
void JsonFileSanitizer::JsonFileRead(
const base::FilePath& file_path,
std::tuple<std::string, bool, bool> read_and_delete_result) {
if (!std::get<1>(read_and_delete_result)) {
ReportError(Status::kFileReadError, std::string());
return;
}
if (!std::get<2>(read_and_delete_result)) {
ReportError(Status::kFileDeleteError, std::string());
return;
}
json_parser_ptr_->Parse(
std::get<0>(read_and_delete_result),
base::BindOnce(&JsonFileSanitizer::JsonParsingDone,
weak_factory_.GetWeakPtr(), file_path));
}
void JsonFileSanitizer::JsonParsingDone(
const base::FilePath& file_path,
base::Optional<base::Value> json_value,
const base::Optional<std::string>& error) {
if (!json_value || !json_value->is_dict()) {
ReportError(Status::kDecodingError, error ? *error : std::string());
return;
}
// Reserialize the JSON and write it back to the original file.
std::string json_string;
JSONStringValueSerializer serializer(&json_string);
serializer.set_pretty_print(true);
if (!serializer.Serialize(*json_value)) {
ReportError(Status::kSerializingError, std::string());
return;
}
int size = static_cast<int>(json_string.length());
base::PostTaskAndReplyWithResult(
extensions::GetExtensionFileTaskRunner().get(), FROM_HERE,
base::BindOnce(&WriteStringToFile, std::move(json_string), file_path),
base::BindOnce(&JsonFileSanitizer::JsonFileWritten,
weak_factory_.GetWeakPtr(), file_path, size));
}
void JsonFileSanitizer::JsonFileWritten(const base::FilePath& file_path,
int expected_size,
int actual_size) {
if (expected_size != actual_size) {
ReportError(Status::kFileWriteError, std::string());
return;
}
// We have finished with this JSON file.
size_t removed_count = file_paths_.erase(file_path);
DCHECK_EQ(1U, removed_count);
if (file_paths_.empty()) {
// This was the last path, we are done.
ReportSuccess();
}
}
void JsonFileSanitizer::ReportSuccess() {
std::move(callback_).Run(Status::kSuccess, std::string());
}
void JsonFileSanitizer::ReportError(Status status, const std::string& error) {
// Prevent any other task from reporting, we want to notify only once.
weak_factory_.InvalidateWeakPtrs();
std::move(callback_).Run(status, error);
}
} // namespace extensions