Commit 490ed7f4 authored by Yusuf Sengul's avatar Yusuf Sengul Committed by Commit Bot

Use installer data argument for installing GCPW from MSI

Bug: 1152690
Change-Id: I1cecd128f89a04eee61076b87d54b3f55e98dde9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2559804Reviewed-by: default avatarRobin Lewis <wrlewis@google.com>
Commit-Queue: Yusuf Sengul <yusufsn@google.com>
Cr-Commit-Position: refs/heads/master@{#831186}
parent 2de81f1c
......@@ -8,6 +8,8 @@
#include <string>
#include "base/files/file_util.h"
#include "base/json/json_string_value_serializer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/common/chrome_version.h"
......@@ -29,11 +31,42 @@ const char kUninstall[] = "uninstall";
const char kEnableStats[] = "enable-stats";
const char kDisableStats[] = "disable-stats";
const char kInstallerData[] = "installerdata";
const char kStandaloneInstall[] = "standalone";
} // namespace switches
namespace {
// Path to the msi json value inside the dictionary which is parsed from
// installer data argument in the command line.
const char kMsiJsonPath[] = "distribution.msi";
// The registry name which is saved to indicate installation source.
const wchar_t kMsiInstall[] = L"msi";
// Parses the json data and returns it as a dictionary. If the json data isn't
// valid, returns nullptr.
base::DictionaryValue* ParseDistributionPreferences(
const std::string& json_data) {
JSONStringValueDeserializer json(json_data);
std::string error;
std::unique_ptr<base::Value> root(json.Deserialize(nullptr, &error));
if (!root.get()) {
LOGFN(WARNING) << "Failed to parse initial prefs file: " << error;
return nullptr;
}
if (!root->is_dict()) {
LOGFN(WARNING) << "Failed to parse installer data file";
return nullptr;
}
return static_cast<base::DictionaryValue*>(root.release());
}
} // namespace
StandaloneInstallerConfigurator::StandaloneInstallerConfigurator()
: is_standalone_installation_(false) {}
: is_msi_installation_(false) {}
StandaloneInstallerConfigurator::~StandaloneInstallerConfigurator() {}
......@@ -51,21 +84,43 @@ StandaloneInstallerConfigurator* StandaloneInstallerConfigurator::Get() {
return *GetInstanceStorage();
}
// Sets the installer source for GCPW. When installed through standalone
// installer, |kStandaloneInstall| switch is present in the commandline
// arguments.
// Sets the installer source for GCPW. When installed through MSI,
// contains installer data file name as argument.
void StandaloneInstallerConfigurator::ConfigureInstallationType(
const base::CommandLine& cmdline) {
base::string16 standalone_install16 =
base::UTF8ToUTF16(switches::kStandaloneInstall);
// There are following scenarios for installations:
// First time install from MSI
// First time install from EXE
// MSIs before this kMsiInstall registry gets auto-updated
// MSIs with kMsiInstall registry gets auto-updated
// EXEs with kMsiInstall registry gets auto-updated
// |kStandaloneInstall| indicates fresh installation.
if (cmdline.HasSwitch(switches::kStandaloneInstall)) {
is_standalone_installation_ = true;
HRESULT hr = SetUpdaterClientsAppPathFlag(standalone_install16, 1);
base::Value* is_msi = nullptr;
if (cmdline.HasSwitch(switches::kInstallerData)) {
base::FilePath prefs_path(
cmdline.GetSwitchValuePath(switches::kInstallerData));
if (InitializeFromInstallerData(prefs_path))
is_msi = installer_data_dictionary_->FindPath(kMsiJsonPath);
}
is_msi_installation_ = false;
if (is_msi && is_msi->is_bool() && is_msi->GetBool()) {
is_msi_installation_ = true;
}
} else {
// Honor the registry if it is found, otherwise fall back to MSI
// installation.
is_msi_installation_ =
GetUpdaterClientsAppPathFlagOrDefault(kMsiInstall, 1);
}
HRESULT hr =
SetUpdaterClientsAppPathFlag(kMsiInstall, is_msi_installation_ ? 1 : 0);
if (FAILED(hr))
LOGFN(ERROR) << "SetGlobalFlag failed" << putHR(hr);
} else if (GetUpdaterClientsAppPathFlagOrDefault(standalone_install16, 0)) {
is_standalone_installation_ = true;
}
}
base::string16 StandaloneInstallerConfigurator::GetCurrentDate() {
......@@ -84,14 +139,14 @@ base::string16 StandaloneInstallerConfigurator::GetCurrentDate() {
}
bool StandaloneInstallerConfigurator::IsStandaloneInstallation() const {
return is_standalone_installation_;
return !is_msi_installation_;
}
HRESULT StandaloneInstallerConfigurator::AddUninstallKey(
const base::FilePath& install_path) {
LOGFN(VERBOSE);
if (!is_standalone_installation_)
if (is_msi_installation_)
return S_OK;
std::wstring uninstall_reg = kRegUninstall;
......@@ -242,4 +297,28 @@ HRESULT StandaloneInstallerConfigurator::RemoveUninstallKey() {
return S_OK;
}
bool StandaloneInstallerConfigurator::InitializeFromInstallerData(
base::FilePath prefs_path) {
std::string json_data;
if (base::PathExists(prefs_path) &&
!base::ReadFileToString(prefs_path, &json_data)) {
LOGFN(ERROR) << "Failed to read preferences from " << prefs_path.value();
return false;
}
if (json_data.empty()) {
LOGFN(WARNING) << "Installer data is empty!";
return false;
}
installer_data_dictionary_.reset(ParseDistributionPreferences(json_data));
if (!installer_data_dictionary_) {
LOGFN(WARNING) << "Installer data is empty!";
return false;
}
return true;
}
} // namespace credential_provider
......@@ -11,6 +11,11 @@
#include "base/files/file.h"
#include "base/win/windows_types.h"
namespace base {
class DictionaryValue;
class FilePath;
} // namespace base
namespace credential_provider {
namespace switches {
......@@ -36,8 +41,12 @@ extern const char kUninstall[];
extern const char kEnableStats[];
extern const char kDisableStats[];
// Switch that indicates the fresh installation.
extern const char kStandaloneInstall[];
// Dynamic install parameter switch which is only set for MSIs.
extern const char kInstallerData[];
} // namespace switches
class StandaloneInstallerConfigurator {
......@@ -63,10 +72,17 @@ class StandaloneInstallerConfigurator {
base::string16 GetCurrentDate();
bool is_standalone_installation_;
};
// Parse the provided installer data argument and load into
// |installer_data_dictionary_|.
bool InitializeFromInstallerData(base::FilePath prefs_path);
bool IsStandaloneInstallation(const base::CommandLine& command_line);
// Indicates that GCPW installation source is MSI.
bool is_msi_installation_;
// Dictionary is parsed from the installer data argument which is set only for
// MSIs.
std::unique_ptr<base::DictionaryValue> installer_data_dictionary_;
};
} // namespace credential_provider
......
......@@ -100,6 +100,15 @@ class GcpSetupTest : public ::testing::Test {
return uninstall_reg;
}
base::FilePath CreateFilePath(const std::string& file_name) {
return temp_dir_.GetPath().AppendASCII(file_name.c_str());
}
void CreateJsonFile(const base::FilePath& path, const std::string& data) {
ASSERT_EQ(static_cast<int>(data.size()),
base::WriteFile(path, data.data(), data.size()));
}
void assert_addremove_reg_exists() {
base::win::RegKey uninstall_key;
ASSERT_EQ(ERROR_SUCCESS,
......@@ -166,9 +175,9 @@ class GcpSetupTest : public ::testing::Test {
ASSERT_EQ(version_minor, static_cast<DWORD>(version_components[3]));
}
private:
void SetUp() override;
private:
base::string16 GetCurrentDateForTesting() {
static const wchar_t kDateFormat[] = L"yyyyMMdd";
wchar_t date_str[base::size(kDateFormat)] = {0};
......@@ -192,6 +201,7 @@ class GcpSetupTest : public ::testing::Test {
base::ScopedTempDir scoped_temp_prog_dir_;
base::ScopedTempDir scoped_temp_start_menu_dir_;
base::ScopedTempDir scoped_temp_progdata_dir_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<base::ScopedPathOverride> program_files_override_;
std::unique_ptr<base::ScopedPathOverride> start_menu_override_;
std::unique_ptr<base::ScopedPathOverride> programdata_override_;
......@@ -344,6 +354,8 @@ void GcpSetupTest::SetUp() {
programdata_override_.reset(new base::ScopedPathOverride(
base::DIR_COMMON_APP_DATA, scoped_temp_progdata_dir_.GetPath()));
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
// In non-component builds, base::FILE_MODULE will always return the path
// to base.dll because of the way CURRENT_MODULE works. Therefore overriding
// to point to gaia1_0.dll's destination path (i.e. after it is installed).
......@@ -407,28 +419,37 @@ TEST_F(GcpSetupTest, DoInstallWithExtension) {
}
// Tests install over old install for different types of installations.
// 0 - Indicates that initial installation isn't standalone.
// 1 - Indicates that the initial installation is through a standalone
// installer.
// 0 - Indicates that initial installation is standalone.
// 1 - Indicates that the initial installation is through MSI.
class GcpInstallOverOldInstallTest : public GcpSetupTest,
public ::testing::WithParamInterface<int> {
public:
void SetInstallerConfig(bool append_standalone_flag) {
if (append_standalone_flag) {
void SetInstallerConfig(bool add_installer_data) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
// Indicates fresh installation.
command_line.AppendSwitch(switches::kStandaloneInstall);
if (add_installer_data) {
// Only set if installation source is MSI.
std::string installer_json = "{\"distribution\": {\"msi\": true}}";
base::FilePath installer_data_path = CreateFilePath("myfile.txt");
CreateJsonFile(installer_data_path, installer_json);
command_line.AppendSwitchPath(switches::kInstallerData,
installer_data_path);
}
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line);
}
}
};
TEST_P(GcpInstallOverOldInstallTest, DoInstallOverOldInstall) {
logging::ResetEventSourceForTesting();
// Set installer config as if --standalone flag was provided as indicated by
// test parameter.
// Set installer data argument to indicate the installation source.
SetInstallerConfig(GetParam());
// Install using some old version.
......@@ -449,12 +470,16 @@ TEST_P(GcpInstallOverOldInstallTest, DoInstallOverOldInstall) {
logging::ResetEventSourceForTesting();
// Don't include standalone flag as it should have already been indicated in
// the registry the first time this is called.
SetInstallerConfig(false);
// Don't include any flag to indicate the installation source as
// the registry was set the first time this is called.
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line);
EXPECT_TRUE(
StandaloneInstallerConfigurator::Get()->IsStandaloneInstallation() ==
GetParam());
!GetParam());
// Now install a newer version.
ASSERT_EQ(S_OK,
......@@ -559,6 +584,9 @@ TEST_F(GcpSetupTest, LaunchGcpAfterInstall) {
ExpectAllFilesToExist(false, old_version);
}
// Tests installations from exe and MSI.
// 0 - installation is not standalone.
// 1 - installation is standalone.
class GcpInstallerTest : public GcpSetupTest,
public ::testing::WithParamInterface<int> {};
......@@ -567,13 +595,21 @@ TEST_P(GcpInstallerTest, DoUninstall) {
logging::ResetEventSourceForTesting();
if (standalone_installer) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
command_line.AppendSwitch(switches::kStandaloneInstall);
if (!standalone_installer) {
std::string installer_json = "{\"distribution\": {\"msi\": true}}";
base::FilePath installer_data_path = CreateFilePath("myfile.txt");
CreateJsonFile(installer_data_path, installer_json);
command_line.AppendSwitchPath(switches::kInstallerData,
installer_data_path);
}
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line);
}
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
......@@ -766,6 +802,8 @@ TEST_F(GcpSetupTest, WriteCredentialProviderRegistryValues) {
KEY_ALL_ACCESS));
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
// Set the standalone registry, but not installer data so that EXE
// installation is indicated.
command_line.AppendSwitch(switches::kStandaloneInstall);
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment