blob: cc94cd67b8d7e2d33bee7ac14e1ccb20f9d4c1ce [file] [log] [blame]
// Copyright (c) 2010 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/installer/util/package.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/win/registry.h"
#include "chrome/installer/util/delete_tree_work_item.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/product.h"
using base::win::RegKey;
namespace installer {
Package::Package(const FilePath& path) : path_(path) {
}
Package::~Package() {
}
const FilePath& Package::path() const {
return path_;
}
const Products& Package::products() const {
return products_;
}
bool Package::IsEqual(const FilePath& path) const {
return FilePath::CompareEqualIgnoreCase(path_.value(), path.value());
}
void Package::AssociateProduct(const Product* product) {
#ifndef NDEBUG
for (size_t i = 0; i < products_.size(); ++i) {
DCHECK_EQ(product->system_level(), products_[i]->system_level());
DCHECK_NE(product->distribution()->GetType(),
products_[i]->distribution()->GetType());
}
#endif
products_.push_back(product);
}
bool Package::system_level() const {
// Convenience getter that returns the system_level value of the first
// product for this folder. All distributions must have the same
// value, so the function also checks this in debug builds.
if (!products_.size()) {
NOTREACHED() << "this should not be possible";
return false;
}
return products_[0]->system_level();
}
FilePath Package::GetInstallerDirectory(
const Version& version) const {
return path_.Append(version.GetString())
.Append(installer::kInstallerDir);
}
Version* Package::GetCurrentVersion() const {
scoped_ptr<Version> current_version;
// Be aware that there might be a pending "new_chrome.exe" already in the
// installation path.
FilePath new_chrome_exe(path_.Append(installer::kChromeNewExe));
bool new_chrome_exists = file_util::PathExists(new_chrome_exe);
for (size_t i = 0; i < products_.size(); ++i) {
const Product* product = products_[i];
HKEY root = product->system_level() ? HKEY_LOCAL_MACHINE :
HKEY_CURRENT_USER;
RegKey chrome_key(root, product->distribution()->GetVersionKey().c_str(),
KEY_READ);
std::wstring version;
if (new_chrome_exists)
chrome_key.ReadValue(google_update::kRegOldVersionField, &version);
if (version.empty())
chrome_key.ReadValue(google_update::kRegVersionField, &version);
if (!version.empty()) {
scoped_ptr<Version> this_version(Version::GetVersionFromString(version));
if (this_version.get()) {
if (!current_version.get() ||
current_version->IsHigherThan(this_version.get())) {
current_version.reset(this_version.release());
} else if (current_version.get()) {
DCHECK_EQ(current_version->GetString(), this_version->GetString())
<< "found distributions of different versions in the same "
"installation folder!";
}
}
}
}
return current_version.release();
}
void Package::RemoveOldVersionDirectories(
const Version& latest_version) const {
std::wstring search_path(path_.value());
file_util::AppendToPath(&search_path, L"*");
// TODO(tommi): use file_util::FileEnumerator.
WIN32_FIND_DATA find_file_data;
HANDLE file_handle = FindFirstFile(search_path.c_str(), &find_file_data);
if (file_handle == INVALID_HANDLE_VALUE) {
VLOG(1) << "No directories found under: " << search_path;
return;
}
BOOL ret = TRUE;
scoped_ptr<Version> version;
// We try to delete all directories whose versions are lower than
// latest_version.
while (ret) {
if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
lstrcmpW(find_file_data.cFileName, L"..") != 0 &&
lstrcmpW(find_file_data.cFileName, L".") != 0) {
VLOG(1) << "directory found: " << find_file_data.cFileName;
version.reset(Version::GetVersionFromString(find_file_data.cFileName));
if (version.get() && latest_version.IsHigherThan(version.get())) {
FilePath remove_dir(path_.Append(find_file_data.cFileName));
std::vector<FilePath> key_files;
Products::const_iterator it = products_.begin();
for (; it != products_.end(); ++it) {
BrowserDistribution* dist = it->get()->distribution();
std::vector<FilePath> dist_key_files(dist->GetKeyFiles());
std::vector<FilePath>::const_iterator key_file_iter(
dist_key_files.begin());
for (; key_file_iter != dist_key_files.end(); ++key_file_iter) {
key_files.push_back(remove_dir.Append(*key_file_iter));
}
}
VLOG(1) << "Deleting directory: " << remove_dir.value();
scoped_ptr<DeleteTreeWorkItem> item(
WorkItem::CreateDeleteTreeWorkItem(remove_dir, key_files));
if (!item->Do())
item->Rollback();
}
}
ret = FindNextFile(file_handle, &find_file_data);
}
FindClose(file_handle);
}
} // namespace installer