| // 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/updater/installer.h" |
| |
| #include <utility> |
| |
| #include "base/callback.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "chrome/updater/updater_constants.h" |
| #include "chrome/updater/util.h" |
| #include "components/crx_file/crx_verifier.h" |
| #include "components/update_client/update_client_errors.h" |
| #include "components/update_client/utils.h" |
| |
| namespace updater { |
| |
| namespace { |
| |
| // Version "0" corresponds to no installed version. |
| const char kNullVersion[] = "0.0.0.0"; |
| |
| // Returns the full path to the installation directory for the application |
| // identified by the |crx_id|. |
| base::FilePath GetAppInstallDir(const std::string& crx_id) { |
| base::FilePath app_install_dir; |
| if (GetProductDirectory(&app_install_dir)) { |
| app_install_dir = app_install_dir.AppendASCII(kAppsDir); |
| app_install_dir = app_install_dir.AppendASCII(crx_id); |
| } |
| return app_install_dir; |
| } |
| |
| } // namespace |
| |
| Installer::InstallInfo::InstallInfo() : version(kNullVersion) {} |
| Installer::InstallInfo::~InstallInfo() = default; |
| |
| Installer::Installer(const std::vector<uint8_t>& pk_hash) |
| : pk_hash_(pk_hash), |
| crx_id_(update_client::GetCrxIdFromPublicKeyHash(pk_hash)), |
| install_info_(std::make_unique<InstallInfo>()) {} |
| |
| Installer::~Installer() = default; |
| |
| update_client::CrxComponent Installer::MakeCrxComponent() { |
| update_client::CrxComponent component; |
| component.installer = scoped_refptr<Installer>(this); |
| component.requires_network_encryption = false; |
| component.crx_format_requirement = |
| crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF; |
| component.pk_hash = pk_hash_; |
| component.name = crx_id_; |
| component.version = install_info_->version; |
| component.fingerprint = install_info_->fingerprint; |
| return component; |
| } |
| |
| void Installer::FindInstallOfApp() { |
| VLOG(1) << __func__ << " for " << crx_id_; |
| |
| const base::FilePath app_install_dir = GetAppInstallDir(crx_id_); |
| if (app_install_dir.empty() || !base::PathExists(app_install_dir)) { |
| install_info_ = std::make_unique<InstallInfo>(); |
| return; |
| } |
| |
| base::Version latest_version(kNullVersion); |
| base::FilePath latest_path; |
| std::vector<base::FilePath> older_paths; |
| base::FileEnumerator file_enumerator(app_install_dir, false, |
| base::FileEnumerator::DIRECTORIES); |
| for (auto path = file_enumerator.Next(); !path.value().empty(); |
| path = file_enumerator.Next()) { |
| const base::Version version(path.BaseName().MaybeAsASCII()); |
| |
| // Ignore folders that don't have valid version names. |
| if (!version.IsValid()) |
| continue; |
| |
| // The |version| not newer than the latest found version is marked for |
| // removal. |kNullVersion| is also removed. |
| if (version.CompareTo(latest_version) <= 0) { |
| older_paths.push_back(path); |
| continue; |
| } |
| |
| // New valid |version| folder found. |
| if (!latest_path.empty()) |
| older_paths.push_back(latest_path); |
| |
| latest_version = version; |
| latest_path = path; |
| } |
| |
| install_info_->version = latest_version; |
| install_info_->install_dir = latest_path; |
| install_info_->manifest = update_client::ReadManifest(latest_path); |
| base::ReadFileToString(latest_path.AppendASCII("manifest.fingerprint"), |
| &install_info_->fingerprint); |
| |
| for (const auto& older_path : older_paths) |
| base::DeleteFile(older_path, true); |
| } |
| |
| Installer::Result Installer::InstallHelper(const base::FilePath& unpack_path) { |
| auto local_manifest = update_client::ReadManifest(unpack_path); |
| if (!local_manifest) |
| return Result(update_client::InstallError::BAD_MANIFEST); |
| |
| std::string version_ascii; |
| local_manifest->GetStringASCII("version", &version_ascii); |
| const base::Version manifest_version(version_ascii); |
| |
| VLOG(1) << "Installed version=" << install_info_->version.GetString() |
| << ", installing version=" << manifest_version.GetString(); |
| |
| if (!manifest_version.IsValid()) |
| return Result(update_client::InstallError::INVALID_VERSION); |
| |
| if (install_info_->version.CompareTo(manifest_version) > 0) |
| return Result(update_client::InstallError::VERSION_NOT_UPGRADED); |
| |
| const base::FilePath app_install_dir = GetAppInstallDir(crx_id_); |
| if (app_install_dir.empty()) |
| return Result(update_client::InstallError::NO_DIR_COMPONENT_USER); |
| if (!base::CreateDirectory(app_install_dir)) { |
| return Result( |
| static_cast<int>(update_client::InstallError::CUSTOM_ERROR_BASE) + |
| kCustomInstallErrorCreateAppInstallDirectory); |
| } |
| |
| const auto versioned_install_dir = |
| app_install_dir.AppendASCII(manifest_version.GetString()); |
| if (base::PathExists(versioned_install_dir)) { |
| if (!base::DeleteFile(versioned_install_dir, true)) |
| return Result(update_client::InstallError::CLEAN_INSTALL_DIR_FAILED); |
| } |
| |
| VLOG(1) << "Install_path=" << versioned_install_dir.AsUTF8Unsafe(); |
| |
| if (!base::Move(unpack_path, versioned_install_dir)) { |
| PLOG(ERROR) << "Move failed."; |
| base::DeleteFile(versioned_install_dir, true); |
| return Result(update_client::InstallError::MOVE_FILES_ERROR); |
| } |
| |
| DCHECK(!base::PathExists(unpack_path)); |
| DCHECK(base::PathExists(versioned_install_dir)); |
| |
| install_info_->manifest = std::move(local_manifest); |
| install_info_->version = manifest_version; |
| install_info_->install_dir = versioned_install_dir; |
| base::ReadFileToString( |
| versioned_install_dir.AppendASCII("manifest.fingerprint"), |
| &install_info_->fingerprint); |
| |
| return Result(update_client::InstallError::NONE); |
| } |
| |
| void Installer::OnUpdateError(int error) { |
| LOG(ERROR) << "updater error: " << error << " for " << crx_id_; |
| } |
| |
| void Installer::Install(const base::FilePath& unpack_path, |
| const std::string& public_key, |
| Callback callback) { |
| std::unique_ptr<base::DictionaryValue> manifest; |
| base::Version version; |
| base::FilePath install_path; |
| |
| const auto result = InstallHelper(unpack_path); |
| base::DeleteFile(unpack_path, true); |
| std::move(callback).Run(result); |
| } |
| |
| bool Installer::GetInstalledFile(const std::string& file, |
| base::FilePath* installed_file) { |
| return false; |
| } |
| |
| bool Installer::Uninstall() { |
| return false; |
| } |
| |
| } // namespace updater |