OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/installer/util/installer_state.h" | 5 #include "chrome/installer/util/installer_state.h" |
6 | 6 |
| 7 #include <algorithm> |
| 8 #include <functional> |
| 9 #include <utility> |
| 10 |
| 11 #include "base/command_line.h" |
| 12 #include "base/file_util.h" |
7 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/scoped_ptr.h" |
| 15 #include "base/string_util.h" |
| 16 #include "base/utf_string_conversions.h" |
| 17 #include "chrome/installer/util/delete_tree_work_item.h" |
| 18 #include "chrome/installer/util/helper.h" |
8 #include "chrome/installer/util/installation_state.h" | 19 #include "chrome/installer/util/installation_state.h" |
9 #include "chrome/installer/util/master_preferences.h" | 20 #include "chrome/installer/util/master_preferences.h" |
10 #include "chrome/installer/util/master_preferences_constants.h" | 21 #include "chrome/installer/util/master_preferences_constants.h" |
11 #include "chrome/installer/util/package_properties.h" | 22 #include "chrome/installer/util/product.h" |
| 23 #include "chrome/installer/util/work_item.h" |
12 | 24 |
13 namespace installer { | 25 namespace installer { |
14 | 26 |
15 bool InstallerState::IsMultiInstallUpdate(const MasterPreferences& prefs, | 27 bool InstallerState::IsMultiInstallUpdate(const MasterPreferences& prefs, |
16 const InstallationState& machine_state) { | 28 const InstallationState& machine_state) { |
17 // First, is the package present? | 29 // First, is the package present? |
18 const ProductState* package = | 30 const ProductState* package = |
19 machine_state.GetMultiPackageState(system_install_); | 31 machine_state.GetProductState(level_ == SYSTEM_LEVEL, |
| 32 BrowserDistribution::CHROME_BINARIES); |
20 if (package == NULL) { | 33 if (package == NULL) { |
21 // The multi-install package has not been installed, so it certainly isn't | 34 // The multi-install package has not been installed, so it certainly isn't |
22 // being updated. | 35 // being updated. |
23 return false; | 36 return false; |
24 } | 37 } |
25 | 38 |
26 BrowserDistribution::Type types[2]; | 39 BrowserDistribution::Type types[2]; |
27 size_t num_types = 0; | 40 size_t num_types = 0; |
28 if (prefs.install_chrome()) | 41 if (prefs.install_chrome()) |
29 types[num_types++] = BrowserDistribution::CHROME_BROWSER; | 42 types[num_types++] = BrowserDistribution::CHROME_BROWSER; |
30 if (prefs.install_chrome_frame()) | 43 if (prefs.install_chrome_frame()) |
31 types[num_types++] = BrowserDistribution::CHROME_FRAME; | 44 types[num_types++] = BrowserDistribution::CHROME_FRAME; |
32 | 45 |
33 for (const BrowserDistribution::Type* scan = &types[0], | 46 for (const BrowserDistribution::Type* scan = &types[0], |
34 *end = &types[num_types]; scan != end; ++scan) { | 47 *end = &types[num_types]; scan != end; ++scan) { |
35 const ProductState* product = | 48 const ProductState* product = |
36 machine_state.GetProductState(system_install_, *scan); | 49 machine_state.GetProductState(level_ == SYSTEM_LEVEL, *scan); |
37 if (product == NULL) { | 50 if (product == NULL) { |
38 VLOG(2) << "It seems that distribution type " << *scan | 51 VLOG(2) << "It seems that distribution type " << *scan |
39 << " is being installed for the first time."; | 52 << " is being installed for the first time."; |
40 return false; | 53 return false; |
41 } | 54 } |
42 if (!product->channel().Equals(package->channel())) { | 55 if (!product->channel().Equals(package->channel())) { |
43 VLOG(2) << "It seems that distribution type " << *scan | 56 VLOG(2) << "It seems that distribution type " << *scan |
44 << " is being over installed."; | 57 << " is being over installed."; |
45 return false; | 58 return false; |
46 } | 59 } |
47 } | 60 } |
48 | 61 |
49 VLOG(2) << "It seems that the package is being updated."; | 62 VLOG(2) << "It seems that the package is being updated."; |
50 | 63 |
51 return true; | 64 return true; |
52 } | 65 } |
53 | 66 |
54 InstallerState::InstallerState() : operation_(UNINITIALIZED) { | 67 InstallerState::InstallerState() |
55 } | 68 : operation_(UNINITIALIZED), |
56 | 69 multi_package_distribution_(NULL), |
57 void InstallerState::Initialize(const MasterPreferences& prefs, | 70 level_(UNKNOWN_LEVEL), |
| 71 package_type_(UNKNOWN_PACKAGE_TYPE), |
| 72 root_key_(NULL), |
| 73 msi_(false), |
| 74 verbose_logging_(false) { |
| 75 } |
| 76 |
| 77 InstallerState::InstallerState(Level level) |
| 78 : operation_(UNINITIALIZED), |
| 79 multi_package_distribution_(NULL), |
| 80 level_(UNKNOWN_LEVEL), |
| 81 package_type_(UNKNOWN_PACKAGE_TYPE), |
| 82 root_key_(NULL), |
| 83 msi_(false), |
| 84 verbose_logging_(false) { |
| 85 // Use set_level() so that root_key_ is updated properly. |
| 86 set_level(level); |
| 87 } |
| 88 |
| 89 void InstallerState::Initialize(const CommandLine& command_line, |
| 90 const MasterPreferences& prefs, |
58 const InstallationState& machine_state) { | 91 const InstallationState& machine_state) { |
59 if (!prefs.GetBool(installer::master_preferences::kSystemLevel, | 92 bool pref_bool; |
60 &system_install_)) | 93 if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool)) |
61 system_install_ = false; | 94 pref_bool = false; |
| 95 set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL); |
| 96 |
| 97 if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_)) |
| 98 verbose_logging_ = false; |
| 99 |
| 100 if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool)) |
| 101 pref_bool = false; |
| 102 set_package_type(pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE); |
| 103 |
| 104 if (!prefs.GetBool(master_preferences::kMsi, &msi_)) |
| 105 msi_ = false; |
| 106 |
| 107 const bool is_uninstall = command_line.HasSwitch(switches::kUninstall); |
| 108 |
| 109 if (prefs.install_chrome()) { |
| 110 Product* p = |
| 111 AddProductFromPreferences(BrowserDistribution::CHROME_BROWSER, prefs, |
| 112 machine_state); |
| 113 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") |
| 114 << " distribution: " << p->distribution()->GetApplicationName(); |
| 115 } |
| 116 if (prefs.install_chrome_frame()) { |
| 117 Product* p = |
| 118 AddProductFromPreferences(BrowserDistribution::CHROME_FRAME, prefs, |
| 119 machine_state); |
| 120 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") |
| 121 << " distribution: " << p->distribution()->GetApplicationName(); |
| 122 } |
62 | 123 |
63 BrowserDistribution* operand = NULL; | 124 BrowserDistribution* operand = NULL; |
64 | 125 |
65 if (!prefs.is_multi_install()) { | 126 if (is_uninstall) { |
| 127 operation_ = UNINSTALL; |
| 128 } else if (!prefs.is_multi_install()) { |
66 // For a single-install, the current browser dist is the operand. | 129 // For a single-install, the current browser dist is the operand. |
67 operand = BrowserDistribution::GetDistribution(); | 130 operand = BrowserDistribution::GetDistribution(); |
68 operation_ = SINGLE_INSTALL_OR_UPDATE; | 131 operation_ = SINGLE_INSTALL_OR_UPDATE; |
69 } else if (IsMultiInstallUpdate(prefs, machine_state)) { | 132 } else if (IsMultiInstallUpdate(prefs, machine_state)) { |
70 // Updates driven by Google Update take place under the multi-installer's | 133 // Updates driven by Google Update take place under the multi-installer's |
71 // app guid. | 134 // app guid. |
72 installer::ActivePackageProperties package_properties; | 135 operand = multi_package_distribution_; |
73 operation_ = MULTI_UPDATE; | 136 operation_ = MULTI_UPDATE; |
74 state_key_ = package_properties.GetStateKey(); | |
75 } else { | 137 } else { |
76 // Initial and over installs will always take place under one of the | 138 // Initial and over installs will always take place under one of the |
77 // product app guids. Chrome Frame's will be used if only Chrome Frame | 139 // product app guids. Chrome Frame's will be used if only Chrome Frame |
78 // is being installed. In all other cases, Chrome's is used. | 140 // is being installed. In all other cases, Chrome's is used. |
| 141 operation_ = MULTI_INSTALL; |
| 142 } |
| 143 |
| 144 if (operand == NULL) { |
79 operand = BrowserDistribution::GetSpecificDistribution( | 145 operand = BrowserDistribution::GetSpecificDistribution( |
80 prefs.install_chrome() ? | 146 prefs.install_chrome() ? |
81 BrowserDistribution::CHROME_BROWSER : | 147 BrowserDistribution::CHROME_BROWSER : |
82 BrowserDistribution::CHROME_FRAME, | 148 BrowserDistribution::CHROME_FRAME); |
83 prefs); | 149 } |
84 operation_ = MULTI_INSTALL; | 150 |
85 } | 151 state_key_ = operand->GetStateKey(); |
86 | 152 } |
87 if (operand != NULL) { | 153 |
88 state_key_ = operand->GetStateKey(); | 154 void InstallerState::set_level(Level level) { |
89 } | 155 level_ = level; |
| 156 switch (level) { |
| 157 case USER_LEVEL: |
| 158 root_key_ = HKEY_CURRENT_USER; |
| 159 break; |
| 160 case SYSTEM_LEVEL: |
| 161 root_key_ = HKEY_LOCAL_MACHINE; |
| 162 break; |
| 163 default: |
| 164 DCHECK(level == UNKNOWN_LEVEL); |
| 165 level_ = UNKNOWN_LEVEL; |
| 166 root_key_ = NULL; |
| 167 break; |
| 168 } |
| 169 } |
| 170 |
| 171 void InstallerState::set_package_type(PackageType type) { |
| 172 package_type_ = type; |
| 173 switch (type) { |
| 174 case SINGLE_PACKAGE: |
| 175 multi_package_distribution_ = NULL; |
| 176 break; |
| 177 case MULTI_PACKAGE: |
| 178 multi_package_distribution_ = |
| 179 BrowserDistribution::GetSpecificDistribution( |
| 180 BrowserDistribution::CHROME_BINARIES); |
| 181 break; |
| 182 default: |
| 183 DCHECK(type == UNKNOWN_PACKAGE_TYPE); |
| 184 package_type_ = UNKNOWN_PACKAGE_TYPE; |
| 185 multi_package_distribution_ = NULL; |
| 186 break; |
| 187 } |
| 188 } |
| 189 |
| 190 // Returns the Chrome binaries directory for multi-install or |dist|'s directory |
| 191 // otherwise. |
| 192 FilePath InstallerState::GetDefaultProductInstallPath( |
| 193 BrowserDistribution* dist) const { |
| 194 DCHECK(dist); |
| 195 DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE); |
| 196 |
| 197 if (package_type_ == SINGLE_PACKAGE) { |
| 198 return GetChromeInstallPath(system_install(), dist); |
| 199 } else { |
| 200 return GetChromeInstallPath(system_install(), |
| 201 BrowserDistribution::GetSpecificDistribution( |
| 202 BrowserDistribution::CHROME_BINARIES)); |
| 203 } |
| 204 } |
| 205 |
| 206 // Evaluates a product's eligibility for participation in this operation. |
| 207 // We never expect these checks to fail, hence they all terminate the process in |
| 208 // debug builds. See the log messages for details. |
| 209 bool InstallerState::CanAddProduct(const Product& product, |
| 210 const FilePath* product_dir) const { |
| 211 switch (package_type_) { |
| 212 case SINGLE_PACKAGE: |
| 213 if (!products_.empty()) { |
| 214 LOG(DFATAL) << "Cannot process more than one single-install product."; |
| 215 return false; |
| 216 } |
| 217 break; |
| 218 case MULTI_PACKAGE: |
| 219 if (!product.HasOption(kOptionMultiInstall)) { |
| 220 LOG(DFATAL) << "Cannot process a single-install product with a " |
| 221 "multi-install state."; |
| 222 return false; |
| 223 } |
| 224 if (FindProduct(product.distribution()->GetType()) != NULL) { |
| 225 LOG(DFATAL) << "Cannot process more than one product of the same type."; |
| 226 return false; |
| 227 } |
| 228 if (!target_path_.empty()) { |
| 229 FilePath default_dir; |
| 230 if (product_dir == NULL) |
| 231 default_dir = GetDefaultProductInstallPath(product.distribution()); |
| 232 if (!FilePath::CompareEqualIgnoreCase( |
| 233 (product_dir == NULL ? default_dir : *product_dir).value(), |
| 234 target_path_.value())) { |
| 235 LOG(DFATAL) << "Cannot process products in different directories."; |
| 236 return false; |
| 237 } |
| 238 } |
| 239 break; |
| 240 default: |
| 241 DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_); |
| 242 break; |
| 243 } |
| 244 return true; |
| 245 } |
| 246 |
| 247 // Adds |product|, installed in |product_dir| to this object's collection. If |
| 248 // |product_dir| is NULL, the product's default install location is used. |
| 249 // Returns NULL if |product| is incompatible with this object. Otherwise, |
| 250 // returns a pointer to the product (ownership is held by this object). |
| 251 Product* InstallerState::AddProductInDirectory(const FilePath* product_dir, |
| 252 scoped_ptr<Product>* product) { |
| 253 DCHECK(product != NULL); |
| 254 DCHECK(product->get() != NULL); |
| 255 const Product& the_product = *product->get(); |
| 256 |
| 257 if (!CanAddProduct(the_product, product_dir)) |
| 258 return NULL; |
| 259 |
| 260 if (package_type_ == UNKNOWN_PACKAGE_TYPE) { |
| 261 set_package_type(the_product.HasOption(kOptionMultiInstall) ? |
| 262 MULTI_PACKAGE : SINGLE_PACKAGE); |
| 263 } |
| 264 |
| 265 if (target_path_.empty()) { |
| 266 if (product_dir == NULL) |
| 267 target_path_ = GetDefaultProductInstallPath(the_product.distribution()); |
| 268 else |
| 269 target_path_ = *product_dir; |
| 270 } |
| 271 |
| 272 if (state_key_.empty()) |
| 273 state_key_ = the_product.distribution()->GetStateKey(); |
| 274 |
| 275 products_.push_back(product->release()); |
| 276 return products_[products_->size() - 1]; |
| 277 } |
| 278 |
| 279 Product* InstallerState::AddProduct(scoped_ptr<Product>* product) { |
| 280 return AddProductInDirectory(NULL, product); |
| 281 } |
| 282 |
| 283 // Adds a product of type |distribution_type| constructed on the basis of |
| 284 // |prefs|, setting this object's msi flag if the product is represented in |
| 285 // |machine_state| and is msi-installed. Returns the product that was added, |
| 286 // or NULL if |state| is incompatible with this object. Ownership is not passed |
| 287 // to the caller. |
| 288 Product* InstallerState::AddProductFromPreferences( |
| 289 BrowserDistribution::Type distribution_type, |
| 290 const MasterPreferences& prefs, |
| 291 const InstallationState& machine_state) { |
| 292 scoped_ptr<Product> product_ptr( |
| 293 new Product(BrowserDistribution::GetSpecificDistribution( |
| 294 distribution_type))); |
| 295 product_ptr->InitializeFromPreferences(prefs); |
| 296 |
| 297 Product* product = AddProductInDirectory(NULL, &product_ptr); |
| 298 |
| 299 if (product != NULL && !msi_) { |
| 300 const ProductState* product_state = machine_state.GetProductState( |
| 301 system_install(), distribution_type); |
| 302 if (product_state != NULL) |
| 303 msi_ = product_state->is_msi(); |
| 304 } |
| 305 |
| 306 return product; |
| 307 } |
| 308 |
| 309 Product* InstallerState::AddProductFromState( |
| 310 BrowserDistribution::Type type, |
| 311 const ProductState& state) { |
| 312 scoped_ptr<Product> product_ptr( |
| 313 new Product(BrowserDistribution::GetSpecificDistribution(type))); |
| 314 product_ptr->InitializeFromUninstallCommand(state.uninstall_command()); |
| 315 |
| 316 // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory(). |
| 317 FilePath product_dir = state.GetSetupPath().DirName().DirName().DirName(); |
| 318 |
| 319 Product* product = AddProductInDirectory(&product_dir, &product_ptr); |
| 320 |
| 321 if (product != NULL) |
| 322 msi_ |= state.is_msi(); |
| 323 |
| 324 return product; |
| 325 } |
| 326 |
| 327 bool InstallerState::system_install() const { |
| 328 DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL); |
| 329 return level_ == SYSTEM_LEVEL; |
| 330 } |
| 331 |
| 332 bool InstallerState::is_multi_install() const { |
| 333 DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE); |
| 334 return package_type_ != SINGLE_PACKAGE; |
| 335 } |
| 336 |
| 337 bool InstallerState::RemoveProduct(const Product* product) { |
| 338 ScopedVector<Product>::iterator it = |
| 339 std::find(products_.begin(), products_.end(), product); |
| 340 if (it != products_.end()) { |
| 341 products_->erase(it); |
| 342 return true; |
| 343 } |
| 344 return false; |
| 345 } |
| 346 |
| 347 const Product* InstallerState::FindProduct( |
| 348 BrowserDistribution::Type distribution_type) const { |
| 349 for (Products::const_iterator scan = products_.begin(), end = products_.end(); |
| 350 scan != end; ++scan) { |
| 351 if ((*scan)->is_type(distribution_type)) |
| 352 return *scan; |
| 353 } |
| 354 return NULL; |
| 355 } |
| 356 |
| 357 Version* InstallerState::GetCurrentVersion( |
| 358 const InstallationState& machine_state) const { |
| 359 DCHECK(!products_.empty()); |
| 360 scoped_ptr<Version> current_version; |
| 361 const BrowserDistribution::Type prod_type = (package_type_ == MULTI_PACKAGE) ? |
| 362 BrowserDistribution::CHROME_BINARIES : |
| 363 products_[0]->distribution()->GetType(); |
| 364 const ProductState* product_state = |
| 365 machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type); |
| 366 |
| 367 if (product_state != NULL) { |
| 368 const Version* version = NULL; |
| 369 |
| 370 // Be aware that there might be a pending "new_chrome.exe" already in the |
| 371 // installation path. If so, we use old_version, which holds the version of |
| 372 // "chrome.exe" itself. |
| 373 if (file_util::PathExists(target_path().Append(kChromeNewExe))) |
| 374 version = product_state->old_version(); |
| 375 |
| 376 if (version == NULL) |
| 377 version = &product_state->version(); |
| 378 |
| 379 current_version.reset(version->Clone()); |
| 380 } |
| 381 |
| 382 return current_version.release(); |
| 383 } |
| 384 |
| 385 FilePath InstallerState::GetInstallerDirectory(const Version& version) const { |
| 386 return target_path().Append(ASCIIToWide(version.GetString())) |
| 387 .Append(kInstallerDir); |
| 388 } |
| 389 |
| 390 void InstallerState::RemoveOldVersionDirectories( |
| 391 const Version& latest_version) const { |
| 392 file_util::FileEnumerator version_enum(target_path(), false, |
| 393 file_util::FileEnumerator::DIRECTORIES); |
| 394 scoped_ptr<Version> version; |
| 395 std::vector<FilePath> key_files; |
| 396 |
| 397 // We try to delete all directories whose versions are lower than |
| 398 // latest_version. |
| 399 FilePath next_version = version_enum.Next(); |
| 400 while (!next_version.empty()) { |
| 401 file_util::FileEnumerator::FindInfo find_data = {0}; |
| 402 version_enum.GetFindInfo(&find_data); |
| 403 VLOG(1) << "directory found: " << find_data.cFileName; |
| 404 version.reset(Version::GetVersionFromString( |
| 405 WideToASCII(find_data.cFileName))); |
| 406 if (version.get() && latest_version.CompareTo(*version) > 0) { |
| 407 key_files.clear(); |
| 408 std::for_each(products_.begin(), products_.end(), |
| 409 std::bind2nd(std::mem_fun(&Product::AddKeyFiles), |
| 410 &key_files)); |
| 411 const std::vector<FilePath>::iterator end = key_files.end(); |
| 412 for (std::vector<FilePath>::iterator scan = key_files.begin(); |
| 413 scan != end; ++scan) { |
| 414 *scan = next_version.Append(*scan); |
| 415 } |
| 416 |
| 417 VLOG(1) << "Deleting directory: " << next_version.value(); |
| 418 |
| 419 scoped_ptr<WorkItem> item( |
| 420 WorkItem::CreateDeleteTreeWorkItem(next_version, key_files)); |
| 421 if (!item->Do()) |
| 422 item->Rollback(); |
| 423 } |
| 424 |
| 425 next_version = version_enum.Next(); |
| 426 } |
| 427 } |
| 428 |
| 429 void InstallerState::AddComDllList(std::vector<FilePath>* com_dll_list) const { |
| 430 std::for_each(products_.begin(), products_.end(), |
| 431 std::bind2nd(std::mem_fun(&Product::AddComDllList), |
| 432 com_dll_list)); |
| 433 } |
| 434 |
| 435 bool InstallerState::SetChannelFlags(bool set, |
| 436 ChannelInfo* channel_info) const { |
| 437 bool modified = false; |
| 438 for (Products::const_iterator scan = products_.begin(), end = products_.end(); |
| 439 scan != end; ++scan) { |
| 440 modified |= (*scan)->SetChannelFlags(set, channel_info); |
| 441 } |
| 442 return modified; |
90 } | 443 } |
91 | 444 |
92 } // namespace installer | 445 } // namespace installer |
OLD | NEW |