blob: b3ddd332b798dce63ed4908d4aa21b97f7f33068 [file] [log] [blame]
// Copyright 2019 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 "chrome/browser/browser_switcher/browser_switcher_service_win.h"
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/win/registry.h"
#include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
#include "chrome/browser/browser_switcher/browser_switcher_sitelist.h"
#include "chrome/browser/browser_switcher/ieem_sitelist_parser.h"
#include "chrome/browser/profiles/profile.h"
namespace browser_switcher {
namespace {
const wchar_t kIeSiteListKey[] =
L"SOFTWARE\\Policies\\Microsoft\\Internet Explorer\\Main\\EnterpriseMode";
const wchar_t kIeSiteListValue[] = L"SiteList";
const int kCurrentFileVersion = 1;
base::FilePath GetCacheDir() {
base::FilePath path;
if (!base::PathService::Get(base::DIR_LOCAL_APP_DATA, &path))
return path;
path = path.AppendASCII("Google");
path = path.AppendASCII("BrowserSwitcher");
return path;
}
// Serialize prefs to a string for writing to cache.dat.
std::string SerializeCacheFile(const BrowserSwitcherPrefs& prefs) {
std::ostringstream buffer;
buffer << kCurrentFileVersion << std::endl;
buffer << prefs.GetAlternativeBrowserPath() << std::endl;
buffer << base::JoinString(prefs.GetAlternativeBrowserParameters(), " ")
<< std::endl;
buffer << prefs.GetChromePath() << std::endl;
buffer << base::JoinString(prefs.GetChromeParameters(), " ") << std::endl;
const auto& rules = prefs.GetRules();
buffer << rules.sitelist.size() << std::endl;
if (!rules.sitelist.empty())
buffer << base::JoinString(rules.sitelist, "\n") << std::endl;
buffer << rules.greylist.size() << std::endl;
if (!rules.greylist.empty())
buffer << base::JoinString(rules.greylist, "\n") << std::endl;
return buffer.str();
}
std::string SerializeSitelistCacheFile(
const BrowserSwitcherSitelist* sitelist) {
const auto* ieem = sitelist->GetIeemSitelist();
const auto* external = sitelist->GetExternalSitelist();
std::ostringstream buffer;
buffer << kCurrentFileVersion << std::endl;
buffer << (ieem->sitelist.size() + external->sitelist.size()) << std::endl;
if (!ieem->sitelist.empty())
buffer << base::JoinString(ieem->sitelist, "\n") << std::endl;
if (!external->sitelist.empty())
buffer << base::JoinString(external->sitelist, "\n") << std::endl;
return buffer.str();
}
void SaveDataToFile(const std::string& data, base::StringPiece file_name) {
base::FilePath dir = GetCacheDir();
if (dir.empty())
return;
// Ensure the directory exists.
bool success = base::CreateDirectory(dir);
UMA_HISTOGRAM_BOOLEAN("BrowserSwitcher.CacheFile.MkDirSuccess", success);
if (!success) {
LOG(ERROR) << "Could not create directory: " << dir.LossyDisplayName();
return;
}
base::FilePath tmp_path;
success = base::CreateTemporaryFileInDir(dir, &tmp_path);
UMA_HISTOGRAM_BOOLEAN("BrowserSwitcher.CacheFile.MkTempSuccess", success);
if (!success) {
LOG(ERROR) << "Could not open file for writing: "
<< tmp_path.LossyDisplayName();
return;
}
base::WriteFile(tmp_path, data.c_str(), data.size());
base::FilePath dest_path = dir.AppendASCII(file_name);
success = base::Move(tmp_path, dest_path);
UMA_HISTOGRAM_BOOLEAN("BrowserSwitcher.CacheFile.MoveSuccess", success);
}
void DoRemovePrefsFile() {
base::FilePath dir = GetCacheDir();
if (dir.empty())
return;
// Ignore errors while deleting.
base::FilePath dest_path = dir.AppendASCII("cache.dat");
base::DeleteFile(dest_path, false);
}
// URL to fetch the IEEM sitelist from. Only used for testing.
base::Optional<std::string>* IeemSitelistUrlForTesting() {
static base::NoDestructor<base::Optional<std::string>>
ieem_sitelist_url_for_testing;
return ieem_sitelist_url_for_testing.get();
}
} // namespace
BrowserSwitcherServiceWin::BrowserSwitcherServiceWin(Profile* profile)
: BrowserSwitcherService(profile), weak_ptr_factory_(this) {
if (prefs().IsEnabled())
SavePrefsToFile();
else
DeletePrefsFile();
prefs_subscription_ =
prefs().RegisterPrefsChangedCallback(base::BindRepeating(
&BrowserSwitcherServiceWin::OnBrowserSwitcherPrefsChanged,
base::Unretained(this)));
}
BrowserSwitcherServiceWin::~BrowserSwitcherServiceWin() = default;
void BrowserSwitcherServiceWin::OnBrowserSwitcherPrefsChanged(
BrowserSwitcherPrefs* prefs) {
if (prefs->IsEnabled())
SavePrefsToFile();
else
DeletePrefsFile();
}
// static
void BrowserSwitcherServiceWin::SetIeemSitelistUrlForTesting(
const std::string& spec) {
*IeemSitelistUrlForTesting() = spec;
}
std::vector<RulesetSource> BrowserSwitcherServiceWin::GetRulesetSources() {
auto sources = BrowserSwitcherService::GetRulesetSources();
if (!prefs().UseIeSitelist())
return sources;
GURL sitelist_url = GetIeemSitelistUrl();
if (!sitelist_url.is_valid())
return sources;
sources.emplace_back(
sitelist_url,
base::BindOnce(&BrowserSwitcherServiceWin::OnIeemSitelistParsed,
weak_ptr_factory_.GetWeakPtr()));
return sources;
}
void BrowserSwitcherServiceWin::OnAllRulesetsParsed() {
base::PostTaskWithTraits(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::BindOnce(&SaveDataToFile, SerializeSitelistCacheFile(sitelist()),
"sitelistcache.dat"));
}
// static
GURL BrowserSwitcherServiceWin::GetIeemSitelistUrl() {
if (*IeemSitelistUrlForTesting() != base::nullopt)
return GURL((*IeemSitelistUrlForTesting()).value());
base::win::RegKey key;
if (ERROR_SUCCESS != key.Open(HKEY_LOCAL_MACHINE, kIeSiteListKey, KEY_READ) &&
ERROR_SUCCESS != key.Open(HKEY_CURRENT_USER, kIeSiteListKey, KEY_READ)) {
return GURL();
}
std::wstring url_string;
if (ERROR_SUCCESS != key.ReadValue(kIeSiteListValue, &url_string))
return GURL();
return GURL(base::UTF16ToUTF8(url_string));
}
void BrowserSwitcherServiceWin::OnIeemSitelistParsed(ParsedXml xml) {
if (xml.error) {
LOG(ERROR) << "Unable to parse IEEM SiteList: " << *xml.error;
} else {
VLOG(2) << "Done parsing IEEM SiteList. "
<< "Applying rules to future navigations.";
sitelist()->SetIeemSitelist(std::move(xml));
}
ieem_downloader_.reset();
}
void BrowserSwitcherServiceWin::SavePrefsToFile() {
base::PostTaskWithTraits(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::BindOnce(&SaveDataToFile, SerializeCacheFile(prefs()),
"cache.dat"));
}
void BrowserSwitcherServiceWin::DeletePrefsFile() const {
base::PostTaskWithTraits(FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::BindOnce(&DoRemovePrefsFile));
}
} // namespace browser_switcher