OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 // |
| 5 // This file contains the definitions of the installer functions that build |
| 6 // the WorkItemList used to install the application. |
| 7 |
| 8 #include "chrome/installer/setup/install_worker.h" |
| 9 |
| 10 #include <shlobj.h> |
| 11 #include <time.h> |
| 12 #include <vector> |
| 13 |
| 14 #include "base/command_line.h" |
| 15 #include "base/file_path.h" |
| 16 #include "base/logging.h" |
| 17 #include "base/path_service.h" |
| 18 #include "base/string_util.h" |
| 19 #include "base/utf_string_conversions.h" |
| 20 #include "base/version.h" |
| 21 #include "base/win/registry.h" |
| 22 #include "chrome/installer/setup/install.h" |
| 23 #include "chrome/installer/setup/setup_constants.h" |
| 24 #include "chrome/installer/util/chrome_frame_distribution.h" |
| 25 #include "chrome/installer/util/conditional_work_item_list.h" |
| 26 #include "chrome/installer/util/create_reg_key_work_item.h" |
| 27 #include "chrome/installer/util/google_update_constants.h" |
| 28 #include "chrome/installer/util/helper.h" |
| 29 #include "chrome/installer/util/installation_state.h" |
| 30 #include "chrome/installer/util/installer_state.h" |
| 31 #include "chrome/installer/util/install_util.h" |
| 32 #include "chrome/installer/util/master_preferences.h" |
| 33 #include "chrome/installer/util/master_preferences_constants.h" |
| 34 #include "chrome/installer/util/package.h" |
| 35 #include "chrome/installer/util/package_properties.h" |
| 36 #include "chrome/installer/util/product.h" |
| 37 #include "chrome/installer/util/set_reg_value_work_item.h" |
| 38 #include "chrome/installer/util/shell_util.h" |
| 39 #include "chrome/installer/util/util_constants.h" |
| 40 #include "chrome/installer/util/work_item_list.h" |
| 41 |
| 42 using base::win::RegKey; |
| 43 |
| 44 namespace { |
| 45 |
| 46 // This method tells if we are running on 64 bit platform so that we can copy |
| 47 // one extra exe. If the API call to determine 64 bit fails, we play it safe |
| 48 // and return true anyway so that the executable can be copied. |
| 49 bool Is64bit() { |
| 50 typedef BOOL (WINAPI* WOW_FUNC)(HANDLE, BOOL*); |
| 51 BOOL is_64 = FALSE; |
| 52 |
| 53 HMODULE module = GetModuleHandle(L"kernel32.dll"); |
| 54 WOW_FUNC is_wow64 = reinterpret_cast<WOW_FUNC>( |
| 55 GetProcAddress(module, "IsWow64Process")); |
| 56 return (is_wow64 != NULL) && |
| 57 (!(is_wow64)(GetCurrentProcess(), &is_64) || (is_64 != FALSE)); |
| 58 } |
| 59 |
| 60 } // namespace |
| 61 |
| 62 namespace installer { |
| 63 |
| 64 // Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of |
| 65 // products managed by a given package. |
| 66 void AddRegisterComDllWorkItemsForPackage(const Package& package, |
| 67 const Version* old_version, |
| 68 const Version& new_version, |
| 69 WorkItemList* work_item_list) { |
| 70 // First collect the list of DLLs to be registered from each product. |
| 71 const Products& products = package.products(); |
| 72 Products::const_iterator product_iter(products.begin()); |
| 73 std::vector<FilePath> com_dll_list; |
| 74 for (; product_iter != products.end(); ++product_iter) { |
| 75 BrowserDistribution* dist = product_iter->get()->distribution(); |
| 76 std::vector<FilePath> dist_dll_list(dist->GetComDllList()); |
| 77 com_dll_list.insert(com_dll_list.end(), dist_dll_list.begin(), |
| 78 dist_dll_list.end()); |
| 79 } |
| 80 |
| 81 // Then, if we got some, attempt to unregister the DLLs from the old |
| 82 // version directory and then re-register them in the new one. |
| 83 // Note that if we are migrating the install directory then we will not |
| 84 // successfully unregister the old DLLs. |
| 85 // TODO(robertshield): See whether we need to fix the migration case. |
| 86 // TODO(robertshield): If we ever remove a DLL from a product, this will |
| 87 // not unregister it on update. We should build the unregistration list from |
| 88 // saved state instead of assuming it is the same as the registration list. |
| 89 if (!com_dll_list.empty()) { |
| 90 if (old_version) { |
| 91 FilePath old_dll_path( |
| 92 package.path().Append(UTF8ToWide(old_version->GetString()))); |
| 93 |
| 94 installer::AddRegisterComDllWorkItems(old_dll_path, |
| 95 com_dll_list, |
| 96 package.system_level(), |
| 97 false, // Unregister |
| 98 true, // May fail |
| 99 work_item_list); |
| 100 } |
| 101 |
| 102 FilePath dll_path( |
| 103 package.path().Append(UTF8ToWide(new_version.GetString()))); |
| 104 installer::AddRegisterComDllWorkItems(dll_path, |
| 105 com_dll_list, |
| 106 package.system_level(), |
| 107 true, // Register |
| 108 false, // Must succeed. |
| 109 work_item_list); |
| 110 } |
| 111 } |
| 112 |
| 113 void AddInstallerCopyTasks(const FilePath& setup_path, |
| 114 const FilePath& archive_path, |
| 115 const FilePath& temp_path, |
| 116 const Version& new_version, |
| 117 WorkItemList* install_list, |
| 118 const Package& package) { |
| 119 DCHECK(install_list); |
| 120 FilePath installer_dir(package.GetInstallerDirectory(new_version)); |
| 121 install_list->AddCreateDirWorkItem(installer_dir); |
| 122 |
| 123 FilePath exe_dst(installer_dir.Append(setup_path.BaseName())); |
| 124 FilePath archive_dst(installer_dir.Append(archive_path.BaseName())); |
| 125 |
| 126 install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(), |
| 127 temp_path.value(), WorkItem::ALWAYS); |
| 128 if (package.system_level()) { |
| 129 install_list->AddCopyTreeWorkItem(archive_path.value(), archive_dst.value(), |
| 130 temp_path.value(), WorkItem::ALWAYS); |
| 131 } else { |
| 132 install_list->AddMoveTreeWorkItem(archive_path.value(), archive_dst.value(), |
| 133 temp_path.value()); |
| 134 } |
| 135 } |
| 136 |
| 137 // This method adds work items to create (or update) Chrome uninstall entry in |
| 138 // either the Control Panel->Add/Remove Programs list or in the Omaha client |
| 139 // state key if running under an MSI installer. |
| 140 void AddUninstallShortcutWorkItems(const FilePath& setup_path, |
| 141 const Version& new_version, |
| 142 WorkItemList* install_list, |
| 143 const Product& product) { |
| 144 HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : |
| 145 HKEY_CURRENT_USER; |
| 146 BrowserDistribution* browser_dist = product.distribution(); |
| 147 DCHECK(browser_dist); |
| 148 |
| 149 // When we are installed via an MSI, we need to store our uninstall strings |
| 150 // in the Google Update client state key. We do this even for non-MSI |
| 151 // managed installs to avoid breaking the edge case whereby an MSI-managed |
| 152 // install is updated by a non-msi installer (which would confuse the MSI |
| 153 // machinery if these strings were not also updated). |
| 154 // Do not quote the command line for the MSI invocation. |
| 155 FilePath install_path(product.package().path()); |
| 156 FilePath installer_path( |
| 157 product.package().GetInstallerDirectory(new_version)); |
| 158 installer_path = installer_path.Append(setup_path.BaseName()); |
| 159 |
| 160 CommandLine uninstall_arguments(CommandLine::NO_PROGRAM); |
| 161 AppendUninstallCommandLineFlags(&uninstall_arguments, product); |
| 162 |
| 163 if (product.is_chrome()) { |
| 164 // The Chrome uninstallation command serves as the master uninstall |
| 165 // command for Chrome + all other products (i.e. Chrome Frame) that do |
| 166 // not have an uninstall entry in the Add/Remove Programs dialog. |
| 167 const Products& products = product.package().products(); |
| 168 for (size_t i = 0; i < products.size(); ++i) { |
| 169 const Product& p = *products[i]; |
| 170 if (!p.is_chrome() && !p.ShouldCreateUninstallEntry()) { |
| 171 p.distribution()->AppendUninstallCommandLineFlags(&uninstall_arguments); |
| 172 } |
| 173 } |
| 174 } |
| 175 |
| 176 std::wstring update_state_key(browser_dist->GetStateKey()); |
| 177 install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key); |
| 178 install_list->AddSetRegValueWorkItem(reg_root, update_state_key, |
| 179 installer::kUninstallStringField, installer_path.value(), true); |
| 180 install_list->AddSetRegValueWorkItem(reg_root, update_state_key, |
| 181 installer::kUninstallArgumentsField, |
| 182 uninstall_arguments.command_line_string(), true); |
| 183 |
| 184 if (product.ShouldCreateUninstallEntry()) { |
| 185 // We need to quote the command line for the Add/Remove Programs dialog. |
| 186 CommandLine quoted_uninstall_cmd(installer_path); |
| 187 DCHECK_EQ(quoted_uninstall_cmd.command_line_string()[0], '"'); |
| 188 quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false); |
| 189 |
| 190 std::wstring uninstall_reg = browser_dist->GetUninstallRegPath(); |
| 191 install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg); |
| 192 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| 193 installer::kUninstallDisplayNameField, |
| 194 browser_dist->GetAppShortCutName(), true); |
| 195 install_list->AddSetRegValueWorkItem(reg_root, |
| 196 uninstall_reg, installer::kUninstallStringField, |
| 197 quoted_uninstall_cmd.command_line_string(), true); |
| 198 install_list->AddSetRegValueWorkItem(reg_root, |
| 199 uninstall_reg, |
| 200 L"InstallLocation", |
| 201 install_path.value(), |
| 202 true); |
| 203 |
| 204 // DisplayIcon, NoModify and NoRepair |
| 205 FilePath chrome_icon(install_path.Append(installer::kChromeExe)); |
| 206 ShellUtil::GetChromeIcon(product.distribution(), chrome_icon.value()); |
| 207 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| 208 L"DisplayIcon", chrome_icon.value(), |
| 209 true); |
| 210 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| 211 L"NoModify", static_cast<DWORD>(1), |
| 212 true); |
| 213 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| 214 L"NoRepair", static_cast<DWORD>(1), |
| 215 true); |
| 216 |
| 217 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| 218 L"Publisher", |
| 219 browser_dist->GetPublisherName(), |
| 220 true); |
| 221 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| 222 L"Version", |
| 223 UTF8ToWide(new_version.GetString()), |
| 224 true); |
| 225 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| 226 L"DisplayVersion", |
| 227 UTF8ToWide(new_version.GetString()), |
| 228 true); |
| 229 time_t rawtime = time(NULL); |
| 230 struct tm timeinfo = {0}; |
| 231 localtime_s(&timeinfo, &rawtime); |
| 232 wchar_t buffer[9]; |
| 233 if (wcsftime(buffer, 9, L"%Y%m%d", &timeinfo) == 8) { |
| 234 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| 235 L"InstallDate", |
| 236 buffer, false); |
| 237 } |
| 238 } |
| 239 } |
| 240 |
| 241 // Create Version key for a product (if not already present) and sets the new |
| 242 // product version as the last step. |
| 243 void AddVersionKeyWorkItems(HKEY root, |
| 244 const Product& product, |
| 245 const Version& new_version, |
| 246 WorkItemList* list) { |
| 247 // Create Version key for each distribution (if not already present) and set |
| 248 // the new product version as the last step. |
| 249 std::wstring version_key(product.distribution()->GetVersionKey()); |
| 250 list->AddCreateRegKeyWorkItem(root, version_key); |
| 251 |
| 252 std::wstring product_name(product.distribution()->GetAppShortCutName()); |
| 253 list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField, |
| 254 product_name, true); // overwrite name also |
| 255 list->AddSetRegValueWorkItem(root, version_key, |
| 256 google_update::kRegOopcrashesField, |
| 257 static_cast<DWORD>(1), |
| 258 false); // set during first install |
| 259 list->AddSetRegValueWorkItem(root, version_key, |
| 260 google_update::kRegVersionField, |
| 261 UTF8ToWide(new_version.GetString()), |
| 262 true); // overwrite version |
| 263 } |
| 264 |
| 265 void AddProductSpecificWorkItems(bool install, |
| 266 const FilePath& setup_path, |
| 267 const Version& new_version, |
| 268 const Package& package, |
| 269 WorkItemList* list) { |
| 270 const Products& products = package.products(); |
| 271 for (size_t i = 0; i < products.size(); ++i) { |
| 272 const Product& p = *products[i]; |
| 273 if (p.is_chrome_frame()) { |
| 274 AddChromeFrameWorkItems(install, setup_path, new_version, p, list); |
| 275 } |
| 276 } |
| 277 } |
| 278 |
| 279 // Adds work items that make registry adjustments for Google Update. When a |
| 280 // product is installed (including overinstall), Google Update will write the |
| 281 // channel ("ap") value into either Chrome or Chrome Frame's ClientState key. |
| 282 // In the multi-install case, this value is used as the basis upon which the |
| 283 // package's channel value is built (by adding the ordered list of installed |
| 284 // products and their options). |
| 285 void AddGoogleUpdateWorkItems(const InstallationState& original_state, |
| 286 const InstallerState& installer_state, |
| 287 const Package& package, |
| 288 WorkItemList* install_list) { |
| 289 // Is a multi-install product being installed or over-installed? |
| 290 if (installer_state.operation() != InstallerState::MULTI_INSTALL) |
| 291 return; |
| 292 |
| 293 const HKEY reg_root = package.system_level() ? HKEY_LOCAL_MACHINE : |
| 294 HKEY_CURRENT_USER; |
| 295 const std::wstring key_path = installer_state.state_key(); |
| 296 ChannelInfo channel_info; |
| 297 |
| 298 // Update the "ap" value for the product being installed/updated. |
| 299 // It is completely acceptable for there to be no "ap" value or even no |
| 300 // ClientState key. Note that we check the registry rather than |
| 301 // original_state since on a fresh install the "ap" value will be present |
| 302 // sans "pv" value. |
| 303 channel_info.Initialize(RegKey(reg_root, key_path.c_str(), KEY_QUERY_VALUE)); |
| 304 |
| 305 // This is a multi-install product. |
| 306 bool modified = channel_info.SetMultiInstall(true); |
| 307 |
| 308 // Add the appropriate modifiers for all products and their options. |
| 309 Products::const_iterator scan = package.products().begin(); |
| 310 const Products::const_iterator end = package.products().end(); |
| 311 for (; scan != end; ++scan) { |
| 312 modified |= scan->get()->distribution()->SetChannelFlags(true, |
| 313 &channel_info); |
| 314 } |
| 315 |
| 316 // Write the results if needed. |
| 317 if (modified) { |
| 318 install_list->AddSetRegValueWorkItem(reg_root, key_path, |
| 319 google_update::kRegApField, |
| 320 channel_info.value(), true); |
| 321 } |
| 322 |
| 323 // Synchronize the other products and the package with this one. |
| 324 std::wstring other_key; |
| 325 std::vector<std::wstring> keys; |
| 326 |
| 327 keys.reserve(package.products().size()); |
| 328 other_key = package.properties()->GetStateKey(); |
| 329 if (other_key != key_path) |
| 330 keys.push_back(other_key); |
| 331 scan = package.products().begin(); |
| 332 for (; scan != end; ++scan) { |
| 333 other_key = scan->get()->distribution()->GetStateKey(); |
| 334 if (other_key != key_path) |
| 335 keys.push_back(other_key); |
| 336 } |
| 337 |
| 338 RegKey key; |
| 339 ChannelInfo other_info; |
| 340 std::vector<std::wstring>::const_iterator kscan = keys.begin(); |
| 341 std::vector<std::wstring>::const_iterator kend = keys.end(); |
| 342 for (; kscan != kend; ++kscan) { |
| 343 // Handle the case where the ClientState key doesn't exist by creating it. |
| 344 // This takes care of the multi-installer's package key, which is not |
| 345 // created by Google Update for us. |
| 346 if (!key.Open(reg_root, kscan->c_str(), KEY_QUERY_VALUE) || |
| 347 !other_info.Initialize(key)) { |
| 348 other_info.set_value(std::wstring()); |
| 349 } |
| 350 if (!other_info.Equals(channel_info)) { |
| 351 if (!key.Valid()) |
| 352 install_list->AddCreateRegKeyWorkItem(reg_root, *kscan); |
| 353 install_list->AddSetRegValueWorkItem(reg_root, *kscan, |
| 354 google_update::kRegApField, |
| 355 channel_info.value(), true); |
| 356 } |
| 357 } |
| 358 // TODO(grt): check for other keys/values we should put in the package's |
| 359 // ClientState and/or Clients key. |
| 360 } |
| 361 |
| 362 // This is called when an MSI installation is run. It may be that a user is |
| 363 // attempting to install the MSI on top of a non-MSI managed installation. |
| 364 // If so, try and remove any existing uninstallation shortcuts, as we want the |
| 365 // uninstall to be managed entirely by the MSI machinery (accessible via the |
| 366 // Add/Remove programs dialog). |
| 367 void AddDeleteUninstallShortcutsForMSIWorkItems(const Product& product, |
| 368 WorkItemList* work_item_list) { |
| 369 DCHECK(product.IsMsi()) << "This must only be called for MSI installations!"; |
| 370 |
| 371 // First attempt to delete the old installation's ARP dialog entry. |
| 372 HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : |
| 373 HKEY_CURRENT_USER; |
| 374 base::win::RegKey root_key(reg_root, L"", KEY_ALL_ACCESS); |
| 375 std::wstring uninstall_reg(product.distribution()->GetUninstallRegPath()); |
| 376 |
| 377 WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem( |
| 378 reg_root, uninstall_reg); |
| 379 delete_reg_key->set_ignore_failure(true); |
| 380 |
| 381 // Then attempt to delete the old installation's start menu shortcut. |
| 382 FilePath uninstall_link; |
| 383 if (product.system_level()) { |
| 384 PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link); |
| 385 } else { |
| 386 PathService::Get(base::DIR_START_MENU, &uninstall_link); |
| 387 } |
| 388 |
| 389 if (uninstall_link.empty()) { |
| 390 LOG(ERROR) << "Failed to get location for shortcut."; |
| 391 } else { |
| 392 uninstall_link = uninstall_link.Append( |
| 393 product.distribution()->GetAppShortCutName()); |
| 394 uninstall_link = uninstall_link.Append( |
| 395 product.distribution()->GetUninstallLinkName() + L".lnk"); |
| 396 VLOG(1) << "Deleting old uninstall shortcut (if present): " |
| 397 << uninstall_link.value(); |
| 398 WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem( |
| 399 uninstall_link); |
| 400 delete_link->set_ignore_failure(true); |
| 401 delete_link->set_log_message( |
| 402 "Failed to delete old uninstall shortcut."); |
| 403 } |
| 404 } |
| 405 |
| 406 // After a successful copying of all the files, this function is called to |
| 407 // do a few post install tasks: |
| 408 // - Handle the case of in-use-update by updating "opv" (old version) key or |
| 409 // deleting it if not required. |
| 410 // - Register any new dlls and unregister old dlls. |
| 411 // - If this is an MSI install, ensures that the MSI marker is set, and sets |
| 412 // it if not. |
| 413 // If these operations are successful, the function returns true, otherwise |
| 414 // false. |
| 415 bool AppendPostInstallTasks(bool multi_install, |
| 416 const FilePath& setup_path, |
| 417 const FilePath& new_chrome_exe, |
| 418 const Version* current_version, |
| 419 const Version& new_version, |
| 420 const Package& package, |
| 421 WorkItemList* post_install_task_list) { |
| 422 DCHECK(post_install_task_list); |
| 423 HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : |
| 424 HKEY_CURRENT_USER; |
| 425 const Products& products = package.products(); |
| 426 |
| 427 |
| 428 // Append work items that will only be executed if this was an update. |
| 429 // We update the 'opv' key with the current version that is active and 'cmd' |
| 430 // key with the rename command to run. |
| 431 { |
| 432 scoped_ptr<WorkItemList> in_use_update_work_items( |
| 433 WorkItem::CreateConditionalWorkItemList( |
| 434 new ConditionRunIfFileExists(new_chrome_exe))); |
| 435 in_use_update_work_items->set_log_message("InUseUpdateWorkItemList"); |
| 436 |
| 437 FilePath installer_path(package.GetInstallerDirectory(new_version) |
| 438 .Append(setup_path.BaseName())); |
| 439 |
| 440 CommandLine rename(installer_path); |
| 441 rename.AppendSwitch(installer::switches::kRenameChromeExe); |
| 442 if (package.system_level()) |
| 443 rename.AppendSwitch(installer::switches::kSystemLevel); |
| 444 |
| 445 if (InstallUtil::IsChromeSxSProcess()) |
| 446 rename.AppendSwitch(installer::switches::kChromeSxS); |
| 447 |
| 448 if (multi_install) |
| 449 rename.AppendSwitch(installer::switches::kMultiInstall); |
| 450 |
| 451 std::wstring version_key; |
| 452 for (size_t i = 0; i < products.size(); ++i) { |
| 453 BrowserDistribution* dist = products[i]->distribution(); |
| 454 version_key = dist->GetVersionKey(); |
| 455 |
| 456 if (current_version != NULL) { |
| 457 in_use_update_work_items->AddSetRegValueWorkItem(root, version_key, |
| 458 google_update::kRegOldVersionField, |
| 459 UTF8ToWide(current_version->GetString()), true); |
| 460 } |
| 461 |
| 462 // Adding this registry entry for all products is overkill. |
| 463 // However, as it stands, we don't have a way to know which distribution |
| 464 // will check the key and run the command, so we add it for all. |
| 465 // After the first run, the subsequent runs should just be noops. |
| 466 // (see Upgrade::SwapNewChromeExeIfPresent). |
| 467 in_use_update_work_items->AddSetRegValueWorkItem( |
| 468 root, |
| 469 version_key, |
| 470 google_update::kRegRenameCmdField, |
| 471 rename.command_line_string(), |
| 472 true); |
| 473 } |
| 474 |
| 475 if (multi_install) { |
| 476 PackageProperties* props = package.properties(); |
| 477 if (props->ReceivesUpdates() && current_version != NULL) { |
| 478 in_use_update_work_items->AddSetRegValueWorkItem( |
| 479 root, |
| 480 props->GetVersionKey(), |
| 481 google_update::kRegOldVersionField, |
| 482 UTF8ToWide(current_version->GetString()), |
| 483 true); |
| 484 // TODO(tommi): We should move the rename command here. We also need to |
| 485 // update Upgrade::SwapNewChromeExeIfPresent. |
| 486 } |
| 487 } |
| 488 |
| 489 post_install_task_list->AddWorkItem(in_use_update_work_items.release()); |
| 490 } |
| 491 |
| 492 // Append work items that will be executed if this was NOT an in-use update. |
| 493 { |
| 494 scoped_ptr<WorkItemList> regular_update_work_items( |
| 495 WorkItem::CreateConditionalWorkItemList( |
| 496 new Not(new ConditionRunIfFileExists(new_chrome_exe)))); |
| 497 regular_update_work_items->set_log_message( |
| 498 "RegularUpdateWorkItemList"); |
| 499 |
| 500 // Since this was not an in-use-update, delete 'opv' and 'cmd' keys. |
| 501 for (size_t i = 0; i < products.size(); ++i) { |
| 502 BrowserDistribution* dist = products[i]->distribution(); |
| 503 std::wstring version_key(dist->GetVersionKey()); |
| 504 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key, |
| 505 google_update::kRegOldVersionField, |
| 506 true); |
| 507 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key, |
| 508 google_update::kRegRenameCmdField, |
| 509 true); |
| 510 } |
| 511 |
| 512 post_install_task_list->AddWorkItem(regular_update_work_items.release()); |
| 513 } |
| 514 |
| 515 AddRegisterComDllWorkItemsForPackage(package, current_version, new_version, |
| 516 post_install_task_list); |
| 517 |
| 518 for (size_t i = 0; i < products.size(); ++i) { |
| 519 const Product* product = products[i]; |
| 520 // If we're told that we're an MSI install, make sure to set the marker |
| 521 // in the client state key so that future updates do the right thing. |
| 522 if (product->IsMsi()) { |
| 523 AddSetMsiMarkerWorkItem(*product, true, post_install_task_list); |
| 524 |
| 525 // We want MSI installs to take over the Add/Remove Programs shortcut. |
| 526 // Make a best-effort attempt to delete any shortcuts left over from |
| 527 // previous non-MSI installations for the same type of install (system or |
| 528 // per user). |
| 529 AddDeleteUninstallShortcutsForMSIWorkItems(*product, |
| 530 post_install_task_list); |
| 531 } |
| 532 } |
| 533 |
| 534 return true; |
| 535 } |
| 536 |
| 537 void AddInstallWorkItems(const InstallationState& original_state, |
| 538 const InstallerState& installer_state, |
| 539 bool multi_install, |
| 540 const FilePath& setup_path, |
| 541 const FilePath& archive_path, |
| 542 const FilePath& src_path, |
| 543 const FilePath& temp_dir, |
| 544 const Version& new_version, |
| 545 scoped_ptr<Version>* current_version, |
| 546 const Package& package, |
| 547 WorkItemList* install_list) { |
| 548 DCHECK(install_list); |
| 549 |
| 550 // A temp directory that work items need and the actual install directory. |
| 551 install_list->AddCreateDirWorkItem(temp_dir); |
| 552 install_list->AddCreateDirWorkItem(package.path()); |
| 553 |
| 554 // Delete any new_chrome.exe if present (we will end up creating a new one |
| 555 // if required) and then copy chrome.exe |
| 556 FilePath new_chrome_exe( |
| 557 package.path().Append(installer::kChromeNewExe)); |
| 558 |
| 559 install_list->AddDeleteTreeWorkItem(new_chrome_exe); |
| 560 install_list->AddCopyTreeWorkItem( |
| 561 src_path.Append(installer::kChromeExe).value(), |
| 562 package.path().Append(installer::kChromeExe).value(), |
| 563 temp_dir.value(), WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe.value()); |
| 564 |
| 565 // Extra executable for 64 bit systems. |
| 566 if (Is64bit()) { |
| 567 install_list->AddCopyTreeWorkItem( |
| 568 src_path.Append(installer::kWowHelperExe).value(), |
| 569 package.path().Append(installer::kWowHelperExe).value(), |
| 570 temp_dir.value(), WorkItem::ALWAYS); |
| 571 } |
| 572 |
| 573 // If it is system level install copy the version folder (since we want to |
| 574 // take the permissions of %ProgramFiles% folder) otherwise just move it. |
| 575 if (package.system_level()) { |
| 576 install_list->AddCopyTreeWorkItem( |
| 577 src_path.Append(UTF8ToWide(new_version.GetString())).value(), |
| 578 package.path().Append(UTF8ToWide(new_version.GetString())).value(), |
| 579 temp_dir.value(), WorkItem::ALWAYS); |
| 580 } else { |
| 581 install_list->AddMoveTreeWorkItem( |
| 582 src_path.Append(UTF8ToWide(new_version.GetString())).value(), |
| 583 package.path().Append(UTF8ToWide(new_version.GetString())).value(), |
| 584 temp_dir.value()); |
| 585 } |
| 586 |
| 587 // Copy the default Dictionaries only if the folder doesn't exist already. |
| 588 install_list->AddCopyTreeWorkItem( |
| 589 src_path.Append(installer::kDictionaries).value(), |
| 590 package.path().Append(installer::kDictionaries).value(), |
| 591 temp_dir.value(), WorkItem::IF_NOT_PRESENT); |
| 592 |
| 593 // Delete any old_chrome.exe if present. |
| 594 install_list->AddDeleteTreeWorkItem( |
| 595 package.path().Append(installer::kChromeOldExe)); |
| 596 |
| 597 // Copy installer in install directory and |
| 598 // add shortcut in Control Panel->Add/Remove Programs. |
| 599 AddInstallerCopyTasks(setup_path, archive_path, temp_dir, new_version, |
| 600 install_list, package); |
| 601 |
| 602 HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; |
| 603 |
| 604 const Products& products = package.products(); |
| 605 for (size_t i = 0; i < products.size(); ++i) { |
| 606 const Product* product = products[i]; |
| 607 |
| 608 AddUninstallShortcutWorkItems(setup_path, new_version, install_list, |
| 609 *product); |
| 610 |
| 611 AddVersionKeyWorkItems(root, *product, new_version, install_list); |
| 612 } |
| 613 |
| 614 if (multi_install) { |
| 615 PackageProperties* props = package.properties(); |
| 616 if (props->ReceivesUpdates()) { |
| 617 std::wstring version_key(props->GetVersionKey()); |
| 618 install_list->AddCreateRegKeyWorkItem(root, version_key); |
| 619 install_list->AddSetRegValueWorkItem(root, version_key, |
| 620 google_update::kRegVersionField, |
| 621 UTF8ToWide(new_version.GetString()), |
| 622 true); // overwrite version |
| 623 install_list->AddSetRegValueWorkItem(root, version_key, |
| 624 google_update::kRegNameField, |
| 625 ASCIIToWide(installer::PackageProperties::kPackageProductName), |
| 626 true); // overwrite name also |
| 627 } |
| 628 } |
| 629 |
| 630 // Add any remaining work items that involve special settings for |
| 631 // each product. |
| 632 AddProductSpecificWorkItems(true, setup_path, new_version, package, |
| 633 install_list); |
| 634 |
| 635 AddGoogleUpdateWorkItems(original_state, installer_state, package, |
| 636 install_list); |
| 637 |
| 638 // Append the tasks that run after the installation. |
| 639 AppendPostInstallTasks(multi_install, |
| 640 setup_path, |
| 641 new_chrome_exe, |
| 642 current_version->get(), |
| 643 new_version, |
| 644 package, |
| 645 install_list); |
| 646 } |
| 647 |
| 648 void AddRegisterComDllWorkItems(const FilePath& dll_folder, |
| 649 const std::vector<FilePath>& dll_list, |
| 650 bool system_level, |
| 651 bool do_register, |
| 652 bool ignore_failures, |
| 653 WorkItemList* work_item_list) { |
| 654 DCHECK(work_item_list); |
| 655 if (dll_list.empty()) { |
| 656 VLOG(1) << "No COM DLLs to register"; |
| 657 } else { |
| 658 std::vector<FilePath>::const_iterator dll_iter(dll_list.begin()); |
| 659 for (; dll_iter != dll_list.end(); ++dll_iter) { |
| 660 FilePath dll_path = dll_folder.Append(*dll_iter); |
| 661 WorkItem* work_item = work_item_list->AddSelfRegWorkItem( |
| 662 dll_path.value(), do_register, !system_level); |
| 663 DCHECK(work_item); |
| 664 work_item->set_ignore_failure(ignore_failures); |
| 665 } |
| 666 } |
| 667 } |
| 668 |
| 669 void AddSetMsiMarkerWorkItem(const Product& product, |
| 670 bool set, |
| 671 WorkItemList* work_item_list) { |
| 672 DCHECK(work_item_list); |
| 673 BrowserDistribution* dist = product.distribution(); |
| 674 HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : |
| 675 HKEY_CURRENT_USER; |
| 676 DWORD msi_value = set ? 1 : 0; |
| 677 WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem( |
| 678 reg_root, dist->GetStateKey(), google_update::kRegMSIField, |
| 679 msi_value, true); |
| 680 DCHECK(set_msi_work_item); |
| 681 set_msi_work_item->set_ignore_failure(true); |
| 682 set_msi_work_item->set_log_message("Could not write MSI marker!"); |
| 683 } |
| 684 |
| 685 void AddChromeFrameWorkItems(bool install, |
| 686 const FilePath& setup_path, |
| 687 const Version& new_version, |
| 688 const Product& product, |
| 689 WorkItemList* list) { |
| 690 DCHECK(product.is_chrome_frame()); |
| 691 if (!product.package().multi_install()) { |
| 692 VLOG(1) << "Not adding GCF specific work items for single install."; |
| 693 return; |
| 694 } |
| 695 |
| 696 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); |
| 697 |
| 698 BrowserDistribution* cf = BrowserDistribution::GetSpecificDistribution( |
| 699 BrowserDistribution::CHROME_FRAME, prefs); |
| 700 std::wstring version_key(cf->GetVersionKey()); |
| 701 |
| 702 // TODO(tommi): This assumes we know exactly how ShouldCreateUninstallEntry |
| 703 // is implemented. Since there is logic in ChromeFrameDistribution for how |
| 704 // to determine when this is enabled, this is how we have to figure out if |
| 705 // this feature is enabled right now, but it's a hack and we need a cleaner |
| 706 // way to figure this out. |
| 707 // Note that we cannot just check the master preferences for |
| 708 // kChromeFrameReadyMode, since there are other things that need to be correct |
| 709 // in the environment in order to enable this feature. |
| 710 bool ready_mode = !product.distribution()->ShouldCreateUninstallEntry(); |
| 711 |
| 712 HKEY root = product.package().system_level() ? HKEY_LOCAL_MACHINE : |
| 713 HKEY_CURRENT_USER; |
| 714 bool update_chrome_uninstall_command = false; |
| 715 if (ready_mode) { |
| 716 // If GCF is being installed in ready mode, we write an entry to the |
| 717 // multi-install state key. If the value already exists, we will not |
| 718 // overwrite it since the user might have opted out. |
| 719 list->AddCreateRegKeyWorkItem(root, |
| 720 product.package().properties()->GetStateKey()); |
| 721 list->AddSetRegValueWorkItem(root, |
| 722 product.package().properties()->GetStateKey(), |
| 723 installer::kChromeFrameReadyModeField, |
| 724 static_cast<int64>(install ? 1 : 0), // The value we want to set. |
| 725 install ? false : true); // Overwrite existing value. |
| 726 if (install) { |
| 727 FilePath installer_path(product.package() |
| 728 .GetInstallerDirectory(new_version).Append(setup_path.BaseName())); |
| 729 |
| 730 CommandLine basic_cl(installer_path); |
| 731 basic_cl.AppendSwitch(installer::switches::kChromeFrame); |
| 732 basic_cl.AppendSwitch(installer::switches::kMultiInstall); |
| 733 |
| 734 if (product.package().system_level()) |
| 735 basic_cl.AppendSwitch(installer::switches::kSystemLevel); |
| 736 |
| 737 if (InstallUtil::IsChromeSxSProcess()) |
| 738 basic_cl.AppendSwitch(installer::switches::kChromeSxS); |
| 739 |
| 740 CommandLine temp_opt_out(basic_cl); |
| 741 temp_opt_out.AppendSwitch( |
| 742 installer::switches::kChromeFrameReadyModeTempOptOut); |
| 743 |
| 744 CommandLine end_temp_opt_out(basic_cl); |
| 745 end_temp_opt_out.AppendSwitch( |
| 746 installer::switches::kChromeFrameReadyModeEndTempOptOut); |
| 747 |
| 748 CommandLine opt_out(installer_path); |
| 749 AppendUninstallCommandLineFlags(&opt_out, product); |
| 750 // Force Uninstall silences the prompt to reboot to complete uninstall. |
| 751 opt_out.AppendSwitch(installer::switches::kForceUninstall); |
| 752 |
| 753 CommandLine opt_in(basic_cl); |
| 754 opt_in.AppendSwitch( |
| 755 installer::switches::kChromeFrameReadyModeOptIn); |
| 756 |
| 757 list->AddSetRegValueWorkItem(root, version_key, |
| 758 google_update::kRegCFTempOptOutCmdField, |
| 759 temp_opt_out.command_line_string(), true); |
| 760 list->AddSetRegValueWorkItem(root, version_key, |
| 761 google_update::kRegCFEndTempOptOutCmdField, |
| 762 end_temp_opt_out.command_line_string(), |
| 763 true); |
| 764 list->AddSetRegValueWorkItem(root, version_key, |
| 765 google_update::kRegCFOptOutCmdField, |
| 766 opt_out.command_line_string(), true); |
| 767 list->AddSetRegValueWorkItem(root, version_key, |
| 768 google_update::kRegCFOptInCmdField, |
| 769 opt_in.command_line_string(), true); |
| 770 } else { |
| 771 // If Chrome is not also being uninstalled, we need to update its command |
| 772 // line so that it doesn't include uninstalling Chrome Frame now. |
| 773 update_chrome_uninstall_command = |
| 774 (installer::FindProduct(product.package().products(), |
| 775 BrowserDistribution::CHROME_BROWSER) == NULL); |
| 776 } |
| 777 } else { |
| 778 // It doesn't matter here if we're installing or uninstalling Chrome Frame. |
| 779 // If ready mode isn't specified on the command line for installs, we need |
| 780 // to delete the ready mode flag from the registry if it exists - this |
| 781 // constitutes an opt-in for the user. If we're uninstalling CF and ready |
| 782 // mode isn't specified on the command line, that means that CF wasn't |
| 783 // installed with ready mode enabled (the --ready-mode switch should be set |
| 784 // in the registry) so deleting the value should have no effect. |
| 785 // In both cases (install/uninstall), we need to make sure that Chrome's |
| 786 // uninstallation command line does not include the --chrome-frame switch |
| 787 // so that uninstalling Chrome will no longer uninstall Chrome Frame. |
| 788 |
| 789 if (RegKey(root, product.package().properties()->GetStateKey().c_str(), |
| 790 KEY_QUERY_VALUE).Valid()) { |
| 791 list->AddDeleteRegValueWorkItem(root, |
| 792 product.package().properties()->GetStateKey(), |
| 793 installer::kChromeFrameReadyModeField, REG_QWORD); |
| 794 } |
| 795 |
| 796 const Product* chrome = installer::FindProduct(product.package().products(), |
| 797 BrowserDistribution::CHROME_BROWSER); |
| 798 if (chrome) { |
| 799 // Chrome is already a part of this installation run, so we can assume |
| 800 // that the uninstallation arguments will be updated correctly. |
| 801 } else { |
| 802 // Chrome is not a part of this installation run, so we have to explicitly |
| 803 // check if Chrome is installed, and if so, update its uninstallation |
| 804 // command lines. |
| 805 BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( |
| 806 BrowserDistribution::CHROME_BROWSER, |
| 807 MasterPreferences::ForCurrentProcess()); |
| 808 update_chrome_uninstall_command = |
| 809 IsInstalledAsMulti(product.system_level(), dist); |
| 810 } |
| 811 } |
| 812 |
| 813 if (!ready_mode || !install) { |
| 814 list->AddDeleteRegValueWorkItem(root, version_key, |
| 815 google_update::kRegCFTempOptOutCmdField, |
| 816 REG_SZ); |
| 817 list->AddDeleteRegValueWorkItem(root, version_key, |
| 818 google_update::kRegCFEndTempOptOutCmdField, |
| 819 REG_SZ); |
| 820 list->AddDeleteRegValueWorkItem(root, version_key, |
| 821 google_update::kRegCFOptOutCmdField, |
| 822 REG_SZ); |
| 823 list->AddDeleteRegValueWorkItem(root, version_key, |
| 824 google_update::kRegCFOptInCmdField, REG_SZ); |
| 825 } |
| 826 |
| 827 if (update_chrome_uninstall_command) { |
| 828 // Chrome is not a part of this installation run, so we have to explicitly |
| 829 // check if Chrome is installed, and if so, update its uninstallation |
| 830 // command lines. |
| 831 BrowserDistribution* chrome_dist = |
| 832 BrowserDistribution::GetSpecificDistribution( |
| 833 BrowserDistribution::CHROME_BROWSER, prefs); |
| 834 const Package& pack = product.package(); |
| 835 scoped_refptr<Package> package(new Package(pack.multi_install(), |
| 836 pack.system_level(), pack.path(), pack.properties())); |
| 837 scoped_refptr<Product> chrome_product(new Product(chrome_dist, package)); |
| 838 AddUninstallShortcutWorkItems(setup_path, new_version, list, |
| 839 *chrome_product.get()); |
| 840 } |
| 841 } |
| 842 |
| 843 void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd, |
| 844 const Product& product) { |
| 845 DCHECK(uninstall_cmd); |
| 846 |
| 847 uninstall_cmd->AppendSwitch(installer::switches::kUninstall); |
| 848 |
| 849 // Append the product-specific uninstall flags. |
| 850 product.distribution()->AppendUninstallCommandLineFlags(uninstall_cmd); |
| 851 if (product.IsMsi()) { |
| 852 uninstall_cmd->AppendSwitch(installer::switches::kMsi); |
| 853 // See comment in uninstall.cc where we check for the kDeleteProfile switch. |
| 854 if (product.is_chrome_frame()) { |
| 855 uninstall_cmd->AppendSwitch(installer::switches::kDeleteProfile); |
| 856 } |
| 857 } |
| 858 if (product.system_level()) |
| 859 uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel); |
| 860 |
| 861 // Propagate switches obtained from preferences as well. |
| 862 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); |
| 863 if (prefs.is_multi_install()) { |
| 864 uninstall_cmd->AppendSwitch(installer::switches::kMultiInstall); |
| 865 } |
| 866 bool value = false; |
| 867 if (prefs.GetBool(installer::master_preferences::kVerboseLogging, |
| 868 &value) && value) |
| 869 uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging); |
| 870 } |
| 871 |
| 872 } // namespace installer |
OLD | NEW |