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 level_(UNKNOWN_LEVEL), |
57 void InstallerState::Initialize(const MasterPreferences& prefs, | 70 package_type_(UNKNOWN_PACKAGE_TYPE), |
| 71 root_key_(NULL), |
| 72 msi_(false), |
| 73 verbose_logging_(false) { |
| 74 } |
| 75 |
| 76 void InstallerState::Initialize(const CommandLine& command_line, |
| 77 const MasterPreferences& prefs, |
58 const InstallationState& machine_state) { | 78 const InstallationState& machine_state) { |
59 if (!prefs.GetBool(installer::master_preferences::kSystemLevel, | 79 bool pref_bool; |
60 &system_install_)) | 80 if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool)) |
61 system_install_ = false; | 81 pref_bool = false; |
| 82 level_ = (pref_bool ? SYSTEM_LEVEL : USER_LEVEL); |
| 83 |
| 84 root_key_ = (level_ == SYSTEM_LEVEL ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER); |
| 85 |
| 86 if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_)) |
| 87 verbose_logging_ = false; |
| 88 |
| 89 if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool)) |
| 90 pref_bool = false; |
| 91 package_type_ = (pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE); |
| 92 |
| 93 if (!prefs.GetBool(master_preferences::kMsi, &msi_)) |
| 94 msi_ = false; |
| 95 |
| 96 const bool is_uninstall = command_line.HasSwitch(switches::kUninstall); |
| 97 |
| 98 if (prefs.install_chrome()) { |
| 99 const Product& p = |
| 100 AddProductFromPreferences(BrowserDistribution::CHROME_BROWSER, prefs, |
| 101 machine_state); |
| 102 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") |
| 103 << " distribution: " << p.distribution()->GetApplicationName(); |
| 104 } |
| 105 if (prefs.install_chrome_frame()) { |
| 106 const Product& p = |
| 107 AddProductFromPreferences(BrowserDistribution::CHROME_FRAME, prefs, |
| 108 machine_state); |
| 109 VLOG(1) << (is_uninstall ? "Uninstall" : "Install") |
| 110 << " distribution: " << p.distribution()->GetApplicationName(); |
| 111 } |
| 112 |
| 113 // Operate in the Chrome Frame directory iff single install and installing |
| 114 // CF. Otherwise, install in Chrome binaries directory. |
| 115 if (package_type_ == SINGLE_PACKAGE && prefs.install_chrome_frame()) |
| 116 target_path_ = GetChromeInstallPath( |
| 117 level_ == SYSTEM_LEVEL, |
| 118 BrowserDistribution::GetSpecificDistribution( |
| 119 BrowserDistribution::CHROME_FRAME)); |
| 120 else |
| 121 target_path_ = GetChromeInstallPath( |
| 122 level_ == SYSTEM_LEVEL, |
| 123 BrowserDistribution::GetSpecificDistribution( |
| 124 BrowserDistribution::CHROME_BINARIES)); |
| 125 |
| 126 if (package_type_ == MULTI_PACKAGE) |
| 127 multi_package_distribution_ = |
| 128 BrowserDistribution::GetSpecificDistribution( |
| 129 BrowserDistribution::CHROME_BINARIES); |
62 | 130 |
63 BrowserDistribution* operand = NULL; | 131 BrowserDistribution* operand = NULL; |
64 | 132 |
65 if (!prefs.is_multi_install()) { | 133 if (is_uninstall) { |
| 134 operation_ = UNINSTALL; |
| 135 } else if (!prefs.is_multi_install()) { |
66 // For a single-install, the current browser dist is the operand. | 136 // For a single-install, the current browser dist is the operand. |
67 operand = BrowserDistribution::GetDistribution(); | 137 operand = BrowserDistribution::GetDistribution(); |
68 operation_ = SINGLE_INSTALL_OR_UPDATE; | 138 operation_ = SINGLE_INSTALL_OR_UPDATE; |
69 } else if (IsMultiInstallUpdate(prefs, machine_state)) { | 139 } else if (IsMultiInstallUpdate(prefs, machine_state)) { |
70 // Updates driven by Google Update take place under the multi-installer's | 140 // Updates driven by Google Update take place under the multi-installer's |
71 // app guid. | 141 // app guid. |
72 installer::ActivePackageProperties package_properties; | 142 operand = multi_package_distribution_; |
73 operation_ = MULTI_UPDATE; | 143 operation_ = MULTI_UPDATE; |
74 state_key_ = package_properties.GetStateKey(); | |
75 } else { | 144 } else { |
76 // Initial and over installs will always take place under one of the | 145 // 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 | 146 // 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. | 147 // is being installed. In all other cases, Chrome's is used. |
| 148 operation_ = MULTI_INSTALL; |
| 149 } |
| 150 |
| 151 if (operand == NULL) { |
79 operand = BrowserDistribution::GetSpecificDistribution( | 152 operand = BrowserDistribution::GetSpecificDistribution( |
80 prefs.install_chrome() ? | 153 prefs.install_chrome() ? |
81 BrowserDistribution::CHROME_BROWSER : | 154 BrowserDistribution::CHROME_BROWSER : |
82 BrowserDistribution::CHROME_FRAME, | 155 BrowserDistribution::CHROME_FRAME); |
83 prefs); | 156 } |
84 operation_ = MULTI_INSTALL; | 157 |
85 } | 158 state_key_ = operand->GetStateKey(); |
86 | 159 } |
87 if (operand != NULL) { | 160 |
88 state_key_ = operand->GetStateKey(); | 161 void InstallerState::ResetProducts() { |
89 } | 162 operation_ = UNINITIALIZED; |
| 163 target_path_.clear(); |
| 164 products_.reset(); |
| 165 multi_package_distribution_ = NULL; |
| 166 package_type_ = UNKNOWN_PACKAGE_TYPE; |
| 167 msi_ = false; |
| 168 } |
| 169 |
| 170 const Product& InstallerState::AddProductFromPreferences( |
| 171 BrowserDistribution::Type distribution_type, |
| 172 const MasterPreferences& prefs, |
| 173 const InstallationState& machine_state) { |
| 174 scoped_ptr<Product> product( |
| 175 new Product(BrowserDistribution::GetSpecificDistribution( |
| 176 distribution_type))); |
| 177 if (!msi_) { |
| 178 const ProductState* product_state = machine_state.GetProductState( |
| 179 level_ == SYSTEM_LEVEL, distribution_type); |
| 180 if (product_state != NULL) |
| 181 msi_ = product_state->msi(); |
| 182 } |
| 183 product->InitializeFromPreferences(prefs); |
| 184 products_.push_back(product.release()); |
| 185 return *products_[products_.size() - 1]; |
| 186 } |
| 187 |
| 188 Product* InstallerState::AddProductFromState( |
| 189 BrowserDistribution::Type type, |
| 190 const ProductState& state) { |
| 191 DCHECK_NE(SINGLE_PACKAGE, package_type_) |
| 192 << "Cannot process more than one single-install product at a time."; |
| 193 scoped_ptr<Product> product( |
| 194 new Product(BrowserDistribution::GetSpecificDistribution(type))); |
| 195 product->InitializeFromUninstallCommand(state.uninstall_command()); |
| 196 products_.push_back(product.release()); |
| 197 if (package_type_ == UNKNOWN_PACKAGE_TYPE) { |
| 198 package_type_ = state.multi_install() ? MULTI_PACKAGE : SINGLE_PACKAGE; |
| 199 if (package_type_ == MULTI_PACKAGE) |
| 200 multi_package_distribution_ = |
| 201 BrowserDistribution::GetSpecificDistribution( |
| 202 BrowserDistribution::CHROME_BINARIES); |
| 203 // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory(). |
| 204 target_path_ = state.GetSetupPath().DirName().DirName().DirName(); |
| 205 // Use the ClientState key of the first product added. |
| 206 state_key_ = product->distribution()->GetStateKey(); |
| 207 } else { |
| 208 DCHECK_EQ(MULTI_PACKAGE, package_type_); |
| 209 DCHECK(state.multi_install()); |
| 210 DCHECK_EQ(target_path_.value(), |
| 211 state.GetSetupPath().DirName().DirName().DirName().value()); |
| 212 } |
| 213 msi_ |= state.msi(); |
| 214 return products_[products_.size() - 1]; |
| 215 } |
| 216 |
| 217 bool InstallerState::RemoveProduct(const Product* product) { |
| 218 ScopedVector<Product>::iterator it = |
| 219 std::find(products_.begin(), products_.end(), product); |
| 220 if (it != products_.end()) { |
| 221 products_->erase(it); |
| 222 return true; |
| 223 } |
| 224 return false; |
| 225 } |
| 226 |
| 227 const Product* InstallerState::FindProduct( |
| 228 BrowserDistribution::Type distribution_type) const { |
| 229 const Products::const_iterator it = |
| 230 std::find_if(products_.begin(), products_.end(), |
| 231 std::bind2nd(std::mem_fun(&Product::is_type), |
| 232 distribution_type)); |
| 233 return it == products_.end() ? NULL : *it; |
| 234 } |
| 235 |
| 236 Version* InstallerState::GetCurrentVersion( |
| 237 const InstallationState& machine_state) const { |
| 238 DCHECK(!products_.empty()); |
| 239 scoped_ptr<Version> current_version; |
| 240 const BrowserDistribution::Type prod_type = (package_type_ == MULTI_PACKAGE) ? |
| 241 BrowserDistribution::CHROME_BINARIES : |
| 242 products_[0]->distribution()->GetType(); |
| 243 const ProductState* product_state = |
| 244 machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type); |
| 245 |
| 246 if (product_state != NULL) { |
| 247 const Version* version = NULL; |
| 248 |
| 249 // Be aware that there might be a pending "new_chrome.exe" already in the |
| 250 // installation path. If so, we use old_version, which holds the version of |
| 251 // "chrome.exe" itself. |
| 252 if (file_util::PathExists(target_path().Append(kChromeNewExe))) |
| 253 version = product_state->old_version(); |
| 254 |
| 255 if (version == NULL) |
| 256 version = &product_state->version(); |
| 257 |
| 258 current_version.reset(version->Clone()); |
| 259 } |
| 260 |
| 261 return current_version.release(); |
| 262 } |
| 263 |
| 264 FilePath InstallerState::GetInstallerDirectory(const Version& version) const { |
| 265 return target_path().Append(ASCIIToWide(version.GetString())) |
| 266 .Append(kInstallerDir); |
| 267 } |
| 268 |
| 269 void InstallerState::RemoveOldVersionDirectories( |
| 270 const Version& latest_version) const { |
| 271 file_util::FileEnumerator version_enum(target_path(), false, |
| 272 file_util::FileEnumerator::DIRECTORIES); |
| 273 scoped_ptr<Version> version; |
| 274 std::vector<FilePath> key_files; |
| 275 |
| 276 // We try to delete all directories whose versions are lower than |
| 277 // latest_version. |
| 278 FilePath next_version = version_enum.Next(); |
| 279 while (!next_version.empty()) { |
| 280 file_util::FileEnumerator::FindInfo find_data = {0}; |
| 281 version_enum.GetFindInfo(&find_data); |
| 282 VLOG(1) << "directory found: " << find_data.cFileName; |
| 283 version.reset(Version::GetVersionFromString( |
| 284 WideToASCII(find_data.cFileName))); |
| 285 if (version.get() && (latest_version.CompareTo(*version) > 0)) { |
| 286 key_files.clear(); |
| 287 std::for_each(products_.begin(), products_.end(), |
| 288 std::bind2nd(std::mem_fun(&Product::AddKeyFiles), |
| 289 &key_files)); |
| 290 const std::vector<FilePath>::iterator end = key_files.end(); |
| 291 for (std::vector<FilePath>::iterator scan = key_files.begin(); |
| 292 scan != end; ++scan) { |
| 293 *scan = next_version.Append(*scan); |
| 294 } |
| 295 |
| 296 VLOG(1) << "Deleting directory: " << next_version.value(); |
| 297 |
| 298 scoped_ptr<WorkItem> item( |
| 299 WorkItem::CreateDeleteTreeWorkItem(next_version, key_files)); |
| 300 if (!item->Do()) |
| 301 item->Rollback(); |
| 302 } |
| 303 |
| 304 next_version = version_enum.Next(); |
| 305 } |
| 306 } |
| 307 |
| 308 void InstallerState::AddComDllList(std::vector<FilePath>* com_dll_list) const { |
| 309 std::for_each(products_.begin(), products_.end(), |
| 310 std::bind2nd(std::mem_fun(&Product::AddComDllList), |
| 311 com_dll_list)); |
| 312 } |
| 313 |
| 314 bool InstallerState::SetChannelFlags(bool set, |
| 315 ChannelInfo* channel_info) const { |
| 316 bool modified = false; |
| 317 |
| 318 // We don't have access to lambdas yet, but we can sure fake it!. |
| 319 struct SetChannelFlagsFunc |
| 320 : public std::unary_function<const Product*, void> { |
| 321 public: |
| 322 SetChannelFlagsFunc(bool set, ChannelInfo* channel_info, bool* modified) |
| 323 : channel_info_(channel_info), modified_(modified), set_(set) {} |
| 324 result_type operator()(argument_type product) const { |
| 325 *modified_ |= product->SetChannelFlags(set_, channel_info_); |
| 326 } |
| 327 private: |
| 328 ChannelInfo* channel_info_; |
| 329 bool* modified_; |
| 330 bool set_; |
| 331 }; |
| 332 |
| 333 std::for_each(products_.begin(), products_.end(), |
| 334 SetChannelFlagsFunc(set, channel_info, &modified)); |
| 335 return modified; |
90 } | 336 } |
91 | 337 |
92 } // namespace installer | 338 } // namespace installer |
OLD | NEW |