install_worker.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright (c) 2012 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 <oaidl.h>
11#include <shlobj.h>
12#include <time.h>
13
14#include <vector>
15
16#include "base/bind.h"
17#include "base/command_line.h"
18#include "base/files/file_path.h"
19#include "base/files/file_util.h"
20#include "base/logging.h"
21#include "base/memory/scoped_ptr.h"
22#include "base/path_service.h"
23#include "base/strings/string_util.h"
24#include "base/strings/utf_string_conversions.h"
25#include "base/version.h"
26#include "base/win/registry.h"
27#include "base/win/scoped_comptr.h"
28#include "base/win/windows_version.h"
29#include "chrome/common/chrome_constants.h"
30#include "chrome/common/chrome_switches.h"
31#include "chrome/installer/setup/install.h"
32#include "chrome/installer/setup/setup_constants.h"
33#include "chrome/installer/setup/setup_util.h"
34#include "chrome/installer/util/browser_distribution.h"
35#include "chrome/installer/util/callback_work_item.h"
36#include "chrome/installer/util/conditional_work_item_list.h"
37#include "chrome/installer/util/create_reg_key_work_item.h"
38#include "chrome/installer/util/firewall_manager_win.h"
39#include "chrome/installer/util/google_update_constants.h"
40#include "chrome/installer/util/helper.h"
41#include "chrome/installer/util/install_util.h"
42#include "chrome/installer/util/installation_state.h"
43#include "chrome/installer/util/installer_state.h"
44#include "chrome/installer/util/l10n_string_util.h"
45#include "chrome/installer/util/product.h"
46#include "chrome/installer/util/set_reg_value_work_item.h"
47#include "chrome/installer/util/shell_util.h"
48#include "chrome/installer/util/util_constants.h"
49#include "chrome/installer/util/work_item_list.h"
50
51using base::ASCIIToWide;
52using base::win::RegKey;
53
54namespace installer {
55
56namespace {
57
58// The version identifying the work done by setup.exe --configure-user-settings
59// on user login by way of Active Setup.  Increase this value if the work done
60// in setup_main.cc's handling of kConfigureUserSettings changes and should be
61// executed again for all users.
62const wchar_t kActiveSetupVersion[] = L"24,0,0,0";
63
64// Although the UUID of the ChromeFrame class is used for the "current" value,
65// this is done only as a convenience; there is no need for the GUID of the Low
66// Rights policies to match the ChromeFrame class's GUID.  Hence, it is safe to
67// use this completely unrelated GUID for the "old" policies.
68const wchar_t kIELowRightsPolicyOldGuid[] =
69    L"{6C288DD7-76FB-4721-B628-56FAC252E199}";
70
71const wchar_t kElevationPolicyKeyPath[] =
72    L"SOFTWARE\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy\\";
73
74void GetOldIELowRightsElevationPolicyKeyPath(base::string16* key_path) {
75  key_path->assign(kElevationPolicyKeyPath,
76                   arraysize(kElevationPolicyKeyPath) - 1);
77  key_path->append(kIELowRightsPolicyOldGuid,
78                   arraysize(kIELowRightsPolicyOldGuid)- 1);
79}
80
81// Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of
82// products managed by a given package.
83// |old_version| can be NULL to indicate no Chrome is currently installed.
84void AddRegisterComDllWorkItemsForPackage(const InstallerState& installer_state,
85                                          const Version* old_version,
86                                          const Version& new_version,
87                                          WorkItemList* work_item_list) {
88  // First collect the list of DLLs to be registered from each product.
89  std::vector<base::FilePath> com_dll_list;
90  installer_state.AddComDllList(&com_dll_list);
91
92  // Then, if we got some, attempt to unregister the DLLs from the old
93  // version directory and then re-register them in the new one.
94  // Note that if we are migrating the install directory then we will not
95  // successfully unregister the old DLLs.
96  // TODO(robertshield): See whether we need to fix the migration case.
97  // TODO(robertshield): If we ever remove a DLL from a product, this will
98  // not unregister it on update. We should build the unregistration list from
99  // saved state instead of assuming it is the same as the registration list.
100  if (!com_dll_list.empty()) {
101    if (old_version) {
102      base::FilePath old_dll_path(installer_state.target_path().AppendASCII(
103          old_version->GetString()));
104
105      installer::AddRegisterComDllWorkItems(old_dll_path,
106                                            com_dll_list,
107                                            installer_state.system_install(),
108                                            false,  // Unregister
109                                            true,   // May fail
110                                            work_item_list);
111    }
112
113    base::FilePath dll_path(installer_state.target_path().AppendASCII(
114        new_version.GetString()));
115    installer::AddRegisterComDllWorkItems(dll_path,
116                                          com_dll_list,
117                                          installer_state.system_install(),
118                                          true,   // Register
119                                          false,  // Must succeed.
120                                          work_item_list);
121  }
122}
123
124void AddInstallerCopyTasks(const InstallerState& installer_state,
125                           const base::FilePath& setup_path,
126                           const base::FilePath& archive_path,
127                           const base::FilePath& temp_path,
128                           const Version& new_version,
129                           WorkItemList* install_list) {
130  DCHECK(install_list);
131  base::FilePath installer_dir(
132      installer_state.GetInstallerDirectory(new_version));
133  install_list->AddCreateDirWorkItem(installer_dir);
134
135  base::FilePath exe_dst(installer_dir.Append(setup_path.BaseName()));
136
137  if (exe_dst != setup_path) {
138    install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(),
139                                      temp_path.value(), WorkItem::ALWAYS);
140  }
141
142  if (installer_state.RequiresActiveSetup()) {
143    // Make a copy of setup.exe with a different name so that Active Setup
144    // doesn't require an admin on XP thanks to Application Compatibility.
145    base::FilePath active_setup_exe(installer_dir.Append(kActiveSetupExe));
146    install_list->AddCopyTreeWorkItem(
147        setup_path.value(), active_setup_exe.value(), temp_path.value(),
148        WorkItem::ALWAYS);
149  }
150
151  // If only the App Host (not even the Chrome Binaries) is being installed,
152  // this must be a user-level App Host piggybacking on system-level Chrome
153  // Binaries. Only setup.exe is required, and only for uninstall.
154  if (installer_state.products().size() != 1 ||
155      !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
156    base::FilePath archive_dst(installer_dir.Append(archive_path.BaseName()));
157    if (archive_path != archive_dst) {
158      // In the past, we copied rather than moved for system level installs so
159      // that the permissions of %ProgramFiles% would be picked up.  Now that
160      // |temp_path| is in %ProgramFiles% for system level installs (and in
161      // %LOCALAPPDATA% otherwise), there is no need to do this for the archive.
162      // Setup.exe, on the other hand, is created elsewhere so it must always be
163      // copied.
164      if (temp_path.IsParent(archive_path)) {
165        install_list->AddMoveTreeWorkItem(archive_path.value(),
166                                          archive_dst.value(),
167                                          temp_path.value(),
168                                          WorkItem::ALWAYS_MOVE);
169      } else {
170        // This may occur when setup is run out of an existing installation
171        // directory. For example, when quick-enabling user-level App Launcher
172        // from system-level Binaries. We can't (and don't want to) remove the
173        // system-level archive.
174        install_list->AddCopyTreeWorkItem(archive_path.value(),
175                                          archive_dst.value(),
176                                          temp_path.value(),
177                                          WorkItem::ALWAYS);
178      }
179    }
180  }
181}
182
183base::string16 GetRegCommandKey(BrowserDistribution* dist,
184                                const wchar_t* name) {
185  base::string16 cmd_key(dist->GetVersionKey());
186  cmd_key.append(1, base::FilePath::kSeparators[0])
187      .append(google_update::kRegCommandsKey)
188      .append(1, base::FilePath::kSeparators[0])
189      .append(name);
190  return cmd_key;
191}
192
193// Adds work items to create (or delete if uninstalling) app commands to launch
194// the app with a switch. The following criteria should be true:
195//  1. The switch takes one parameter.
196//  2. The command send pings.
197//  3. The command is web accessible.
198//  4. The command is run as the user.
199void AddCommandWithParameterWorkItems(const InstallerState& installer_state,
200                                      const InstallationState& machine_state,
201                                      const Version& new_version,
202                                      const Product& product,
203                                      const wchar_t* command_key,
204                                      const wchar_t* app,
205                                      const char* command_with_parameter,
206                                      WorkItemList* work_item_list) {
207  DCHECK(command_key);
208  DCHECK(app);
209  DCHECK(command_with_parameter);
210  DCHECK(work_item_list);
211
212  base::string16 full_cmd_key(
213      GetRegCommandKey(product.distribution(), command_key));
214
215  if (installer_state.operation() == InstallerState::UNINSTALL) {
216    work_item_list->AddDeleteRegKeyWorkItem(installer_state.root_key(),
217                                            full_cmd_key,
218                                            KEY_WOW64_32KEY)
219        ->set_log_message("removing " + base::UTF16ToASCII(command_key) +
220                          " command");
221  } else {
222    CommandLine cmd_line(installer_state.target_path().Append(app));
223    cmd_line.AppendSwitchASCII(command_with_parameter, "%1");
224
225    AppCommand cmd(cmd_line.GetCommandLineString());
226    cmd.set_sends_pings(true);
227    cmd.set_is_web_accessible(true);
228    cmd.set_is_run_as_user(true);
229    cmd.AddWorkItems(installer_state.root_key(), full_cmd_key, work_item_list);
230  }
231}
232
233void AddInstallAppCommandWorkItems(const InstallerState& installer_state,
234                                   const InstallationState& machine_state,
235                                   const Version& new_version,
236                                   const Product& product,
237                                   WorkItemList* work_item_list) {
238  DCHECK(product.is_chrome_app_host());
239  AddCommandWithParameterWorkItems(installer_state, machine_state, new_version,
240                                   product, kCmdInstallApp,
241                                   installer::kChromeAppHostExe,
242                                   ::switches::kInstallFromWebstore,
243                                   work_item_list);
244}
245
246void AddInstallExtensionCommandWorkItem(const InstallerState& installer_state,
247                                        const InstallationState& machine_state,
248                                        const base::FilePath& setup_path,
249                                        const Version& new_version,
250                                        const Product& product,
251                                        WorkItemList* work_item_list) {
252  DCHECK(product.is_chrome());
253  AddCommandWithParameterWorkItems(installer_state, machine_state, new_version,
254                                   product, kCmdInstallExtension,
255                                   installer::kChromeExe,
256                                   ::switches::kLimitedInstallFromWebstore,
257                                   work_item_list);
258}
259
260// A callback invoked by |work_item| that adds firewall rules for Chrome. Rules
261// are left in-place on rollback unless |remove_on_rollback| is true. This is
262// the case for new installs only. Updates and overinstalls leave the rule
263// in-place on rollback since a previous install of Chrome will be used in that
264// case.
265bool AddFirewallRulesCallback(bool system_level,
266                              BrowserDistribution* dist,
267                              const base::FilePath& chrome_path,
268                              bool remove_on_rollback,
269                              const CallbackWorkItem& work_item) {
270  // There is no work to do on rollback if this is not a new install.
271  if (work_item.IsRollback() && !remove_on_rollback)
272    return true;
273
274  scoped_ptr<FirewallManager> manager =
275      FirewallManager::Create(dist, chrome_path);
276  if (!manager) {
277    LOG(ERROR) << "Failed creating a FirewallManager. Continuing with install.";
278    return true;
279  }
280
281  if (work_item.IsRollback()) {
282    manager->RemoveFirewallRules();
283    return true;
284  }
285
286  // Adding the firewall rule is expected to fail for user-level installs on
287  // Vista+. Try anyway in case the installer is running elevated.
288  if (!manager->AddFirewallRules())
289    LOG(ERROR) << "Failed creating a firewall rules. Continuing with install.";
290
291  // Don't abort installation if the firewall rule couldn't be added.
292  return true;
293}
294
295// Adds work items to |list| to create firewall rules.
296void AddFirewallRulesWorkItems(const InstallerState& installer_state,
297                               BrowserDistribution* dist,
298                               bool is_new_install,
299                               WorkItemList* list) {
300  list->AddCallbackWorkItem(
301      base::Bind(&AddFirewallRulesCallback,
302                 installer_state.system_install(),
303                 dist,
304                 installer_state.target_path().Append(kChromeExe),
305                 is_new_install));
306}
307
308// Returns the basic CommandLine to setup.exe for a quick-enable operation on
309// the binaries. This will unconditionally include --multi-install as well as
310// --verbose-logging if the current installation was launched with
311// --verbose-logging.  |setup_path| and |new_version| are optional only when
312// the operation is an uninstall.
313CommandLine GetGenericQuickEnableCommand(
314    const InstallerState& installer_state,
315    const InstallationState& machine_state,
316    const base::FilePath& setup_path,
317    const Version& new_version) {
318  // Only valid for multi-install operations.
319  DCHECK(installer_state.is_multi_install());
320  // Only valid when Chrome Binaries aren't being uninstalled.
321  DCHECK(installer_state.operation() != InstallerState::UNINSTALL ||
322         !installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES));
323  // setup_path and new_version are required when not uninstalling.
324  DCHECK(installer_state.operation() == InstallerState::UNINSTALL ||
325         (!setup_path.empty() && new_version.IsValid()));
326
327  // The path to setup.exe contains the version of the Chrome Binaries, so it
328  // takes a little work to get it right.
329  base::FilePath binaries_setup_path;
330  if (installer_state.operation() == InstallerState::UNINSTALL) {
331    // One or more products are being uninstalled, but not Chrome Binaries.
332    // Use the path to the currently installed Chrome Binaries' setup.exe.
333    const ProductState* product_state = machine_state.GetProductState(
334        installer_state.system_install(),
335        BrowserDistribution::CHROME_BINARIES);
336    DCHECK(product_state);
337    binaries_setup_path = product_state->uninstall_command().GetProgram();
338  } else {
339    // Chrome Binaries are being installed, updated, or otherwise operated on.
340    // Use the path to the given |setup_path| in the normal location of
341    // multi-install Chrome Binaries of the given |version|.
342    binaries_setup_path = installer_state.GetInstallerDirectory(new_version)
343                              .Append(setup_path.BaseName());
344  }
345  DCHECK(!binaries_setup_path.empty());
346
347  CommandLine cmd_line(binaries_setup_path);
348  cmd_line.AppendSwitch(switches::kMultiInstall);
349  if (installer_state.verbose_logging())
350    cmd_line.AppendSwitch(switches::kVerboseLogging);
351  return cmd_line;
352}
353
354// Adds work items to add the "quick-enable-application-host" command to the
355// multi-installer binaries' version key on the basis of the current operation
356// (represented in |installer_state|) and the pre-existing machine configuration
357// (represented in |machine_state|).
358void AddQuickEnableApplicationLauncherWorkItems(
359    const InstallerState& installer_state,
360    const InstallationState& machine_state,
361    const base::FilePath& setup_path,
362    const Version& new_version,
363    WorkItemList* work_item_list) {
364  DCHECK(work_item_list);
365
366  bool will_have_chrome_binaries =
367      WillProductBePresentAfterSetup(installer_state, machine_state,
368                                     BrowserDistribution::CHROME_BINARIES);
369
370  // For system-level binaries there is no way to keep the command state in sync
371  // with the installation/uninstallation of the Application Launcher (which is
372  // always at user-level).  So we do not try to remove the command, i.e., it
373  // will always be installed if the Chrome Binaries are installed.
374  if (will_have_chrome_binaries) {
375    base::string16 cmd_key(
376        GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
377                             BrowserDistribution::CHROME_BINARIES),
378                         kCmdQuickEnableApplicationHost));
379    CommandLine cmd_line(GetGenericQuickEnableCommand(installer_state,
380                                                      machine_state,
381                                                      setup_path,
382                                                      new_version));
383    // kMultiInstall and kVerboseLogging were processed above.
384    cmd_line.AppendSwitch(switches::kChromeAppLauncher);
385    cmd_line.AppendSwitch(switches::kEnsureGoogleUpdatePresent);
386    AppCommand cmd(cmd_line.GetCommandLineString());
387    cmd.set_sends_pings(true);
388    cmd.set_is_web_accessible(true);
389    cmd.set_is_run_as_user(true);
390    cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
391  }
392}
393
394void AddProductSpecificWorkItems(const InstallationState& original_state,
395                                 const InstallerState& installer_state,
396                                 const base::FilePath& setup_path,
397                                 const Version& new_version,
398                                 bool is_new_install,
399                                 WorkItemList* list) {
400  const Products& products = installer_state.products();
401  for (Products::const_iterator it = products.begin(); it < products.end();
402       ++it) {
403    const Product& p = **it;
404    if (p.is_chrome_app_host()) {
405      AddInstallAppCommandWorkItems(installer_state, original_state,
406                                    new_version, p, list);
407    }
408    if (p.is_chrome()) {
409      AddOsUpgradeWorkItems(installer_state, setup_path, new_version, p,
410                            list);
411      AddInstallExtensionCommandWorkItem(installer_state, original_state,
412                                         setup_path, new_version, p, list);
413      AddFirewallRulesWorkItems(
414          installer_state, p.distribution(), is_new_install, list);
415    }
416    if (p.is_chrome_binaries()) {
417      AddQueryEULAAcceptanceWorkItems(
418          installer_state, setup_path, new_version, p, list);
419      AddQuickEnableChromeFrameWorkItems(installer_state, list);
420      AddQuickEnableApplicationLauncherWorkItems(
421          installer_state, original_state, setup_path, new_version, list);
422    }
423  }
424}
425
426// This is called when an MSI installation is run. It may be that a user is
427// attempting to install the MSI on top of a non-MSI managed installation.
428// If so, try and remove any existing uninstallation shortcuts, as we want the
429// uninstall to be managed entirely by the MSI machinery (accessible via the
430// Add/Remove programs dialog).
431void AddDeleteUninstallShortcutsForMSIWorkItems(
432    const InstallerState& installer_state,
433    const Product& product,
434    const base::FilePath& temp_path,
435    WorkItemList* work_item_list) {
436  DCHECK(installer_state.is_msi())
437      << "This must only be called for MSI installations!";
438
439  // First attempt to delete the old installation's ARP dialog entry.
440  HKEY reg_root = installer_state.root_key();
441  base::string16 uninstall_reg(product.distribution()->GetUninstallRegPath());
442
443  WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem(
444      reg_root, uninstall_reg, KEY_WOW64_32KEY);
445  delete_reg_key->set_ignore_failure(true);
446
447  // Then attempt to delete the old installation's start menu shortcut.
448  base::FilePath uninstall_link;
449  if (installer_state.system_install()) {
450    PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link);
451  } else {
452    PathService::Get(base::DIR_START_MENU, &uninstall_link);
453  }
454
455  if (uninstall_link.empty()) {
456    LOG(ERROR) << "Failed to get location for shortcut.";
457  } else {
458    uninstall_link = uninstall_link.Append(
459        product.distribution()->GetStartMenuShortcutSubfolder(
460            BrowserDistribution::SUBFOLDER_CHROME));
461    uninstall_link = uninstall_link.Append(
462        product.distribution()->GetUninstallLinkName() + installer::kLnkExt);
463    VLOG(1) << "Deleting old uninstall shortcut (if present): "
464            << uninstall_link.value();
465    WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem(
466        uninstall_link, temp_path);
467    delete_link->set_ignore_failure(true);
468    delete_link->set_log_message(
469        "Failed to delete old uninstall shortcut.");
470  }
471}
472
473// Adds Chrome specific install work items to |install_list|.
474// |current_version| can be NULL to indicate no Chrome is currently installed.
475void AddChromeWorkItems(const InstallationState& original_state,
476                        const InstallerState& installer_state,
477                        const base::FilePath& setup_path,
478                        const base::FilePath& archive_path,
479                        const base::FilePath& src_path,
480                        const base::FilePath& temp_path,
481                        const Version* current_version,
482                        const Version& new_version,
483                        WorkItemList* install_list) {
484  const base::FilePath& target_path = installer_state.target_path();
485
486  if (current_version) {
487    // Delete the archive from an existing install to save some disk space.  We
488    // make this an unconditional work item since there's no need to roll this
489    // back; if installation fails we'll be moved to the "-full" channel anyway.
490    base::FilePath old_installer_dir(
491        installer_state.GetInstallerDirectory(*current_version));
492    base::FilePath old_archive(
493        old_installer_dir.Append(installer::kChromeArchive));
494    // Don't delete the archive that we are actually installing from.
495    if (archive_path != old_archive) {
496      install_list->AddDeleteTreeWorkItem(old_archive, temp_path)->
497          set_ignore_failure(true);
498    }
499  }
500
501  // Delete any new_chrome.exe if present (we will end up creating a new one
502  // if required) and then copy chrome.exe
503  base::FilePath new_chrome_exe(target_path.Append(installer::kChromeNewExe));
504
505  install_list->AddDeleteTreeWorkItem(new_chrome_exe, temp_path);
506
507  // TODO(grt): Remove this check in M35.
508  if (installer_state.IsChromeFrameRunning(original_state)) {
509    VLOG(1) << "Chrome Frame in use. Copying to new_chrome.exe";
510    install_list->AddCopyTreeWorkItem(
511        src_path.Append(installer::kChromeExe).value(),
512        new_chrome_exe.value(),
513        temp_path.value(),
514        WorkItem::ALWAYS);
515  } else {
516    install_list->AddCopyTreeWorkItem(
517        src_path.Append(installer::kChromeExe).value(),
518        target_path.Append(installer::kChromeExe).value(),
519        temp_path.value(),
520        WorkItem::NEW_NAME_IF_IN_USE,
521        new_chrome_exe.value());
522  }
523
524  // Extra executable for 64 bit systems.
525  // NOTE: We check for "not disabled" so that if the API call fails, we play it
526  // safe and copy the executable anyway.
527  // NOTE: the file wow_helper.exe is only needed for Vista and below.
528  if (base::win::OSInfo::GetInstance()->wow64_status() !=
529      base::win::OSInfo::WOW64_DISABLED &&
530      base::win::GetVersion() <= base::win::VERSION_VISTA) {
531    install_list->AddMoveTreeWorkItem(
532        src_path.Append(installer::kWowHelperExe).value(),
533        target_path.Append(installer::kWowHelperExe).value(),
534        temp_path.value(),
535        WorkItem::ALWAYS_MOVE);
536  }
537
538  // Install kVisualElementsManifest if it is present in |src_path|. No need to
539  // make this a conditional work item as if the file is not there now, it will
540  // never be.
541  if (base::PathExists(
542          src_path.Append(installer::kVisualElementsManifest))) {
543    install_list->AddMoveTreeWorkItem(
544        src_path.Append(installer::kVisualElementsManifest).value(),
545        target_path.Append(installer::kVisualElementsManifest).value(),
546        temp_path.value(),
547        WorkItem::ALWAYS_MOVE);
548  } else {
549    // We do not want to have an old VisualElementsManifest pointing to an old
550    // version directory. Delete it as there wasn't a new one to replace it.
551    install_list->AddDeleteTreeWorkItem(
552        target_path.Append(installer::kVisualElementsManifest),
553        temp_path);
554  }
555
556  // In the past, we copied rather than moved for system level installs so that
557  // the permissions of %ProgramFiles% would be picked up.  Now that |temp_path|
558  // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA%
559  // otherwise), there is no need to do this.
560  // Note that we pass true for check_duplicates to avoid failing on in-use
561  // repair runs if the current_version is the same as the new_version.
562  bool check_for_duplicates = (current_version &&
563                               current_version->Equals(new_version));
564  install_list->AddMoveTreeWorkItem(
565      src_path.AppendASCII(new_version.GetString()).value(),
566      target_path.AppendASCII(new_version.GetString()).value(),
567      temp_path.value(),
568      check_for_duplicates ? WorkItem::CHECK_DUPLICATES :
569                             WorkItem::ALWAYS_MOVE);
570
571  // Delete any old_chrome.exe if present (ignore failure if it's in use).
572  install_list->AddDeleteTreeWorkItem(
573      target_path.Append(installer::kChromeOldExe), temp_path)->
574          set_ignore_failure(true);
575}
576
577// Probes COM machinery to get an instance of delegate_execute.exe's
578// CommandExecuteImpl class.  This is required so that COM purges its cache of
579// the path to the binary, which changes on updates.  This callback
580// unconditionally returns true since an install should not be aborted if the
581// probe fails.
582bool ProbeCommandExecuteCallback(const base::string16& command_execute_id,
583                                 const CallbackWorkItem& work_item) {
584  // Noop on rollback.
585  if (work_item.IsRollback())
586    return true;
587
588  CLSID class_id = {};
589
590  HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id);
591  if (FAILED(hr)) {
592    LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to "
593                   "CLSID; hr=0x" << std::hex << hr;
594  } else {
595    base::win::ScopedComPtr<IUnknown> command_execute_impl;
596    hr = command_execute_impl.CreateInstance(class_id, NULL,
597                                             CLSCTX_LOCAL_SERVER);
598    if (hr != REGDB_E_CLASSNOTREG) {
599      LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x"
600                 << std::hex << hr;
601    }
602  }
603
604  return true;
605}
606
607void AddUninstallDelegateExecuteWorkItems(
608    HKEY root,
609    const base::string16& delegate_execute_path,
610    WorkItemList* list) {
611  VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
612          << root;
613  // Delete both 64 and 32 keys to handle 32->64 or 64->32 migration.
614  list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_32KEY);
615
616  list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_64KEY);
617
618  // In the past, the ICommandExecuteImpl interface and a TypeLib were both
619  // registered.  Remove these since this operation may be updating a machine
620  // that had the old registrations.
621  list->AddDeleteRegKeyWorkItem(root,
622                                L"Software\\Classes\\Interface\\"
623                                L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}",
624                                KEY_WOW64_32KEY);
625  list->AddDeleteRegKeyWorkItem(root,
626                                L"Software\\Classes\\TypeLib\\"
627#if defined(GOOGLE_CHROME_BUILD)
628                                L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}",
629#else
630                                L"{7779FB70-B399-454A-AA1A-BAA850032B10}",
631#endif
632                                KEY_WOW64_32KEY);
633}
634
635// Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0
636// (exclusively -- crrev.com/132596), registered a DelegateExecute class by
637// mistake (with the same GUID as Chrome). The fix stopped registering the bad
638// value, but didn't delete it. This is a problem for users who had installed
639// Canary before 20.0.1106.0 and now have a system-level Chrome, as the
640// left-behind Canary registrations in HKCU mask the HKLM registrations for the
641// same GUID. Cleanup those registrations if they still exist and belong to this
642// Canary (i.e., the registered delegate_execute's path is under |target_path|).
643void CleanupBadCanaryDelegateExecuteRegistration(
644    const base::FilePath& target_path,
645    WorkItemList* list) {
646  base::string16 google_chrome_delegate_execute_path(
647      L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}");
648  base::string16 google_chrome_local_server_32(
649      google_chrome_delegate_execute_path + L"\\LocalServer32");
650
651  RegKey local_server_32_key;
652  base::string16 registered_server;
653  if (local_server_32_key.Open(HKEY_CURRENT_USER,
654                               google_chrome_local_server_32.c_str(),
655                               KEY_QUERY_VALUE) == ERROR_SUCCESS &&
656      local_server_32_key.ReadValue(L"ServerExecutable",
657                                    &registered_server) == ERROR_SUCCESS &&
658      target_path.IsParent(base::FilePath(registered_server))) {
659    scoped_ptr<WorkItemList> no_rollback_list(
660        WorkItem::CreateNoRollbackWorkItemList());
661    AddUninstallDelegateExecuteWorkItems(
662        HKEY_CURRENT_USER, google_chrome_delegate_execute_path,
663        no_rollback_list.get());
664    list->AddWorkItem(no_rollback_list.release());
665    VLOG(1) << "Added deletion items for bad Canary registrations.";
666  }
667}
668
669}  // namespace
670
671// This method adds work items to create (or update) Chrome uninstall entry in
672// either the Control Panel->Add/Remove Programs list or in the Omaha client
673// state key if running under an MSI installer.
674void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
675                                   const base::FilePath& setup_path,
676                                   const Version& new_version,
677                                   const Product& product,
678                                   WorkItemList* install_list) {
679  HKEY reg_root = installer_state.root_key();
680  BrowserDistribution* browser_dist = product.distribution();
681  DCHECK(browser_dist);
682
683  // When we are installed via an MSI, we need to store our uninstall strings
684  // in the Google Update client state key. We do this even for non-MSI
685  // managed installs to avoid breaking the edge case whereby an MSI-managed
686  // install is updated by a non-msi installer (which would confuse the MSI
687  // machinery if these strings were not also updated). The UninstallString
688  // value placed in the client state key is also used by the mini_installer to
689  // locate the setup.exe instance used for binary patching.
690  // Do not quote the command line for the MSI invocation.
691  base::FilePath install_path(installer_state.target_path());
692  base::FilePath installer_path(
693      installer_state.GetInstallerDirectory(new_version));
694  installer_path = installer_path.Append(setup_path.BaseName());
695
696  CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
697  AppendUninstallCommandLineFlags(installer_state, product,
698                                  &uninstall_arguments);
699
700  base::string16 update_state_key(browser_dist->GetStateKey());
701  install_list->AddCreateRegKeyWorkItem(
702      reg_root, update_state_key, KEY_WOW64_32KEY);
703  install_list->AddSetRegValueWorkItem(reg_root,
704                                       update_state_key,
705                                       KEY_WOW64_32KEY,
706                                       installer::kUninstallStringField,
707                                       installer_path.value(),
708                                       true);
709  install_list->AddSetRegValueWorkItem(
710      reg_root,
711      update_state_key,
712      KEY_WOW64_32KEY,
713      installer::kUninstallArgumentsField,
714      uninstall_arguments.GetCommandLineString(),
715      true);
716
717  // MSI installations will manage their own uninstall shortcuts.
718  if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) {
719    // We need to quote the command line for the Add/Remove Programs dialog.
720    CommandLine quoted_uninstall_cmd(installer_path);
721    DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"');
722    quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
723
724    base::string16 uninstall_reg = browser_dist->GetUninstallRegPath();
725    install_list->AddCreateRegKeyWorkItem(
726        reg_root, uninstall_reg, KEY_WOW64_32KEY);
727    install_list->AddSetRegValueWorkItem(reg_root,
728                                         uninstall_reg,
729                                         KEY_WOW64_32KEY,
730                                         installer::kUninstallDisplayNameField,
731                                         browser_dist->GetDisplayName(),
732                                         true);
733    install_list->AddSetRegValueWorkItem(
734        reg_root,
735        uninstall_reg,
736        KEY_WOW64_32KEY,
737        installer::kUninstallStringField,
738        quoted_uninstall_cmd.GetCommandLineString(),
739        true);
740    install_list->AddSetRegValueWorkItem(reg_root,
741                                         uninstall_reg,
742                                         KEY_WOW64_32KEY,
743                                         L"InstallLocation",
744                                         install_path.value(),
745                                         true);
746
747    BrowserDistribution* dist = product.distribution();
748    base::string16 chrome_icon = ShellUtil::FormatIconLocation(
749        install_path.Append(dist->GetIconFilename()).value(),
750        dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME));
751    install_list->AddSetRegValueWorkItem(reg_root,
752                                         uninstall_reg,
753                                         KEY_WOW64_32KEY,
754                                         L"DisplayIcon",
755                                         chrome_icon,
756                                         true);
757    install_list->AddSetRegValueWorkItem(reg_root,
758                                         uninstall_reg,
759                                         KEY_WOW64_32KEY,
760                                         L"NoModify",
761                                         static_cast<DWORD>(1),
762                                         true);
763    install_list->AddSetRegValueWorkItem(reg_root,
764                                         uninstall_reg,
765                                         KEY_WOW64_32KEY,
766                                         L"NoRepair",
767                                         static_cast<DWORD>(1),
768                                         true);
769
770    install_list->AddSetRegValueWorkItem(reg_root,
771                                         uninstall_reg,
772                                         KEY_WOW64_32KEY,
773                                         L"Publisher",
774                                         browser_dist->GetPublisherName(),
775                                         true);
776    install_list->AddSetRegValueWorkItem(reg_root,
777                                         uninstall_reg,
778                                         KEY_WOW64_32KEY,
779                                         L"Version",
780                                         ASCIIToWide(new_version.GetString()),
781                                         true);
782    install_list->AddSetRegValueWorkItem(reg_root,
783                                         uninstall_reg,
784                                         KEY_WOW64_32KEY,
785                                         L"DisplayVersion",
786                                         ASCIIToWide(new_version.GetString()),
787                                         true);
788    // TODO(wfh): Ensure that this value is preserved in the 64-bit hive when
789    // 64-bit installs place the uninstall information into the 64-bit registry.
790    install_list->AddSetRegValueWorkItem(reg_root,
791                                         uninstall_reg,
792                                         KEY_WOW64_32KEY,
793                                         L"InstallDate",
794                                         InstallUtil::GetCurrentDate(),
795                                         false);
796
797    const std::vector<uint16>& version_components = new_version.components();
798    if (version_components.size() == 4) {
799      // Our version should be in major.minor.build.rev.
800      install_list->AddSetRegValueWorkItem(
801          reg_root,
802          uninstall_reg,
803          KEY_WOW64_32KEY,
804          L"VersionMajor",
805          static_cast<DWORD>(version_components[2]),
806          true);
807      install_list->AddSetRegValueWorkItem(
808          reg_root,
809          uninstall_reg,
810          KEY_WOW64_32KEY,
811          L"VersionMinor",
812          static_cast<DWORD>(version_components[3]),
813          true);
814    }
815  }
816}
817
818// Create Version key for a product (if not already present) and sets the new
819// product version as the last step.
820void AddVersionKeyWorkItems(HKEY root,
821                            const base::string16& version_key,
822                            const base::string16& product_name,
823                            const Version& new_version,
824                            bool add_language_identifier,
825                            WorkItemList* list) {
826  list->AddCreateRegKeyWorkItem(root, version_key, KEY_WOW64_32KEY);
827
828  list->AddSetRegValueWorkItem(root,
829                               version_key,
830                               KEY_WOW64_32KEY,
831                               google_update::kRegNameField,
832                               product_name,
833                               true);  // overwrite name also
834  list->AddSetRegValueWorkItem(root,
835                               version_key,
836                               KEY_WOW64_32KEY,
837                               google_update::kRegOopcrashesField,
838                               static_cast<DWORD>(1),
839                               false);  // set during first install
840  if (add_language_identifier) {
841    // Write the language identifier of the current translation.  Omaha's set of
842    // languages is a superset of Chrome's set of translations with this one
843    // exception: what Chrome calls "en-us", Omaha calls "en".  sigh.
844    base::string16 language(GetCurrentTranslation());
845    if (LowerCaseEqualsASCII(language, "en-us"))
846      language.resize(2);
847    list->AddSetRegValueWorkItem(root,
848                                 version_key,
849                                 KEY_WOW64_32KEY,
850                                 google_update::kRegLangField,
851                                 language,
852                                 false);  // do not overwrite language
853  }
854  list->AddSetRegValueWorkItem(root,
855                               version_key,
856                               KEY_WOW64_32KEY,
857                               google_update::kRegVersionField,
858                               ASCIIToWide(new_version.GetString()),
859                               true);  // overwrite version
860}
861
862// Mirror oeminstall the first time anything is installed multi.  There is no
863// need to update the value on future install/update runs since this value never
864// changes.  Note that the value is removed by Google Update after EULA
865// acceptance is processed.
866void AddOemInstallWorkItems(const InstallationState& original_state,
867                            const InstallerState& installer_state,
868                            WorkItemList* install_list) {
869  DCHECK(installer_state.is_multi_install());
870  const bool system_install = installer_state.system_install();
871  if (!original_state.GetProductState(system_install,
872                                      BrowserDistribution::CHROME_BINARIES)) {
873    const HKEY root_key = installer_state.root_key();
874    base::string16 multi_key(
875        installer_state.multi_package_binaries_distribution()->GetStateKey());
876
877    // Copy the value from Chrome unless Chrome isn't installed or being
878    // installed.
879    BrowserDistribution::Type source_type;
880    if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) {
881      source_type = BrowserDistribution::CHROME_BROWSER;
882    } else if (!installer_state.products().empty()) {
883      // Pick a product, any product.
884      source_type = installer_state.products()[0]->distribution()->GetType();
885    } else {
886      // Nothing is being installed?  Entirely unexpected, so do no harm.
887      LOG(ERROR) << "No products found in AddOemInstallWorkItems";
888      return;
889    }
890    const ProductState* source_product =
891        original_state.GetNonVersionedProductState(system_install, source_type);
892
893    base::string16 oem_install;
894    if (source_product->GetOemInstall(&oem_install)) {
895      VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from "
896              << BrowserDistribution::GetSpecificDistribution(source_type)->
897                     GetDisplayName();
898      install_list->AddCreateRegKeyWorkItem(
899          root_key, multi_key, KEY_WOW64_32KEY);
900      // Always overwrite an old value.
901      install_list->AddSetRegValueWorkItem(root_key,
902                                           multi_key,
903                                           KEY_WOW64_32KEY,
904                                           google_update::kRegOemInstallField,
905                                           oem_install,
906                                           true);
907    } else {
908      // Clear any old value.
909      install_list->AddDeleteRegValueWorkItem(
910          root_key,
911          multi_key,
912          KEY_WOW64_32KEY,
913          google_update::kRegOemInstallField);
914    }
915  }
916}
917
918// Mirror eulaaccepted the first time anything is installed multi.  There is no
919// need to update the value on future install/update runs since
920// GoogleUpdateSettings::SetEULAConsent will modify the value for both the
921// relevant product and for the binaries.
922void AddEulaAcceptedWorkItems(const InstallationState& original_state,
923                              const InstallerState& installer_state,
924                              WorkItemList* install_list) {
925  DCHECK(installer_state.is_multi_install());
926  const bool system_install = installer_state.system_install();
927  if (!original_state.GetProductState(system_install,
928                                      BrowserDistribution::CHROME_BINARIES)) {
929    const HKEY root_key = installer_state.root_key();
930    base::string16 multi_key(
931        installer_state.multi_package_binaries_distribution()->GetStateKey());
932
933    // Copy the value from the product with the greatest value.
934    bool have_eula_accepted = false;
935    BrowserDistribution::Type product_type = BrowserDistribution::NUM_TYPES;
936    DWORD eula_accepted = 0;
937    const Products& products = installer_state.products();
938    for (Products::const_iterator it = products.begin(); it < products.end();
939         ++it) {
940      const Product& product = **it;
941      if (product.is_chrome_binaries())
942        continue;
943      DWORD dword_value = 0;
944      BrowserDistribution::Type this_type = product.distribution()->GetType();
945      const ProductState* product_state =
946          original_state.GetNonVersionedProductState(
947              system_install, this_type);
948      if (product_state->GetEulaAccepted(&dword_value) &&
949          (!have_eula_accepted || dword_value > eula_accepted)) {
950        have_eula_accepted = true;
951        eula_accepted = dword_value;
952        product_type = this_type;
953      }
954    }
955
956    if (have_eula_accepted) {
957      VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from "
958              << BrowserDistribution::GetSpecificDistribution(product_type)->
959                     GetDisplayName();
960      install_list->AddCreateRegKeyWorkItem(
961          root_key, multi_key, KEY_WOW64_32KEY);
962      install_list->AddSetRegValueWorkItem(root_key,
963                                           multi_key,
964                                           KEY_WOW64_32KEY,
965                                           google_update::kRegEULAAceptedField,
966                                           eula_accepted,
967                                           true);
968    } else {
969      // Clear any old value.
970      install_list->AddDeleteRegValueWorkItem(
971          root_key,
972          multi_key,
973          KEY_WOW64_32KEY,
974          google_update::kRegEULAAceptedField);
975    }
976  }
977}
978
979// Adds work items that make registry adjustments for Google Update.
980void AddGoogleUpdateWorkItems(const InstallationState& original_state,
981                              const InstallerState& installer_state,
982                              WorkItemList* install_list) {
983  // Is a multi-install product being installed or over-installed?
984  if (installer_state.operation() != InstallerState::MULTI_INSTALL &&
985      installer_state.operation() != InstallerState::MULTI_UPDATE) {
986    VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation();
987    return;
988  }
989
990  const bool system_install = installer_state.system_install();
991  const HKEY root_key = installer_state.root_key();
992  base::string16 multi_key(
993      installer_state.multi_package_binaries_distribution()->GetStateKey());
994
995  // For system-level installs, make sure the ClientStateMedium key for the
996  // binaries exists.
997  if (system_install) {
998    install_list->AddCreateRegKeyWorkItem(
999        root_key,
1000        installer_state.multi_package_binaries_distribution()
1001            ->GetStateMediumKey()
1002            .c_str(),
1003        KEY_WOW64_32KEY);
1004  }
1005
1006  // Creating the ClientState key for binaries, if we're migrating to multi then
1007  // copy over Chrome's brand code if it has one.
1008  if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) {
1009    const ProductState* chrome_product_state =
1010        original_state.GetNonVersionedProductState(
1011            system_install, BrowserDistribution::CHROME_BROWSER);
1012
1013    const base::string16& brand(chrome_product_state->brand());
1014    if (!brand.empty()) {
1015      install_list->AddCreateRegKeyWorkItem(
1016          root_key, multi_key, KEY_WOW64_32KEY);
1017      // Write Chrome's brand code to the multi key. Never overwrite the value
1018      // if one is already present (although this shouldn't happen).
1019      install_list->AddSetRegValueWorkItem(root_key,
1020                                           multi_key,
1021                                           KEY_WOW64_32KEY,
1022                                           google_update::kRegBrandField,
1023                                           brand,
1024                                           false);
1025    }
1026  }
1027
1028  AddOemInstallWorkItems(original_state, installer_state, install_list);
1029  AddEulaAcceptedWorkItems(original_state, installer_state, install_list);
1030  AddUsageStatsWorkItems(original_state, installer_state, install_list);
1031
1032  // TODO(grt): check for other keys/values we should put in the package's
1033  // ClientState and/or Clients key.
1034}
1035
1036void AddUsageStatsWorkItems(const InstallationState& original_state,
1037                            const InstallerState& installer_state,
1038                            WorkItemList* install_list) {
1039  DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL ||
1040         installer_state.operation() == InstallerState::MULTI_UPDATE);
1041
1042  HKEY root_key = installer_state.root_key();
1043  bool value_found = false;
1044  DWORD usagestats = 0;
1045  const Products& products = installer_state.products();
1046
1047  // Search for an existing usagestats value for any product.
1048  for (Products::const_iterator scan = products.begin(), end = products.end();
1049       !value_found && scan != end; ++scan) {
1050    if ((*scan)->is_chrome_binaries())
1051      continue;
1052    BrowserDistribution* dist = (*scan)->distribution();
1053    const ProductState* product_state =
1054        original_state.GetNonVersionedProductState(
1055            installer_state.system_install(), dist->GetType());
1056    value_found = product_state->GetUsageStats(&usagestats);
1057  }
1058
1059  // If a value was found, write it in the appropriate location for the
1060  // binaries and remove all values from the products.
1061  if (value_found) {
1062    base::string16 state_key(
1063        installer_state.multi_package_binaries_distribution()->GetStateKey());
1064    install_list->AddCreateRegKeyWorkItem(root_key, state_key, KEY_WOW64_32KEY);
1065    // Overwrite any existing value so that overinstalls (where Omaha writes a
1066    // new value into a product's state key) pick up the correct value.
1067    install_list->AddSetRegValueWorkItem(root_key,
1068                                         state_key,
1069                                         KEY_WOW64_32KEY,
1070                                         google_update::kRegUsageStatsField,
1071                                         usagestats,
1072                                         true);
1073
1074    for (Products::const_iterator scan = products.begin(), end = products.end();
1075         scan != end; ++scan) {
1076      if ((*scan)->is_chrome_binaries())
1077        continue;
1078      BrowserDistribution* dist = (*scan)->distribution();
1079      if (installer_state.system_install()) {
1080        install_list->AddDeleteRegValueWorkItem(
1081            root_key,
1082            dist->GetStateMediumKey(),
1083            KEY_WOW64_32KEY,
1084            google_update::kRegUsageStatsField);
1085        // Previous versions of Chrome also wrote a value in HKCU even for
1086        // system-level installs, so clean that up.
1087        install_list->AddDeleteRegValueWorkItem(
1088            HKEY_CURRENT_USER,
1089            dist->GetStateKey(),
1090            KEY_WOW64_32KEY,
1091            google_update::kRegUsageStatsField);
1092      }
1093      install_list->AddDeleteRegValueWorkItem(
1094          root_key,
1095          dist->GetStateKey(),
1096          KEY_WOW64_32KEY,
1097          google_update::kRegUsageStatsField);
1098    }
1099  }
1100}
1101
1102bool AppendPostInstallTasks(const InstallerState& installer_state,
1103                            const base::FilePath& setup_path,
1104                            const Version* current_version,
1105                            const Version& new_version,
1106                            const base::FilePath& temp_path,
1107                            WorkItemList* post_install_task_list) {
1108  DCHECK(post_install_task_list);
1109
1110  HKEY root = installer_state.root_key();
1111  const Products& products = installer_state.products();
1112  base::FilePath new_chrome_exe(
1113      installer_state.target_path().Append(installer::kChromeNewExe));
1114
1115  // Append work items that will only be executed if this was an update.
1116  // We update the 'opv' value with the current version that is active,
1117  // the 'cpv' value with the critical update version (if present), and the
1118  // 'cmd' value with the rename command to run.
1119  {
1120    scoped_ptr<WorkItemList> in_use_update_work_items(
1121        WorkItem::CreateConditionalWorkItemList(
1122            new ConditionRunIfFileExists(new_chrome_exe)));
1123    in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
1124
1125    // |critical_version| will be valid only if this in-use update includes a
1126    // version considered critical relative to the version being updated.
1127    Version critical_version(installer_state.DetermineCriticalVersion(
1128        current_version, new_version));
1129    base::FilePath installer_path(
1130        installer_state.GetInstallerDirectory(new_version).Append(
1131            setup_path.BaseName()));
1132
1133    CommandLine rename(installer_path);
1134    rename.AppendSwitch(switches::kRenameChromeExe);
1135    if (installer_state.system_install())
1136      rename.AppendSwitch(switches::kSystemLevel);
1137
1138    if (installer_state.verbose_logging())
1139      rename.AppendSwitch(switches::kVerboseLogging);
1140
1141    base::string16 version_key;
1142    for (size_t i = 0; i < products.size(); ++i) {
1143      BrowserDistribution* dist = products[i]->distribution();
1144      version_key = dist->GetVersionKey();
1145
1146      if (current_version) {
1147        in_use_update_work_items->AddSetRegValueWorkItem(
1148            root,
1149            version_key,
1150            KEY_WOW64_32KEY,
1151            google_update::kRegOldVersionField,
1152            ASCIIToWide(current_version->GetString()),
1153            true);
1154      }
1155      if (critical_version.IsValid()) {
1156        in_use_update_work_items->AddSetRegValueWorkItem(
1157            root,
1158            version_key,
1159            KEY_WOW64_32KEY,
1160            google_update::kRegCriticalVersionField,
1161            ASCIIToWide(critical_version.GetString()),
1162            true);
1163      } else {
1164        in_use_update_work_items->AddDeleteRegValueWorkItem(
1165            root,
1166            version_key,
1167            KEY_WOW64_32KEY,
1168            google_update::kRegCriticalVersionField);
1169      }
1170
1171      // Adding this registry entry for all products (but the binaries) is
1172      // overkill. However, as it stands, we don't have a way to know which
1173      // product will check the key and run the command, so we add it for all.
1174      // The first to run it will perform the operation and clean up the other
1175      // values.
1176      if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) {
1177        CommandLine product_rename_cmd(rename);
1178        products[i]->AppendRenameFlags(&product_rename_cmd);
1179        in_use_update_work_items->AddSetRegValueWorkItem(
1180            root,
1181            version_key,
1182            KEY_WOW64_32KEY,
1183            google_update::kRegRenameCmdField,
1184            product_rename_cmd.GetCommandLineString(),
1185            true);
1186      }
1187    }
1188
1189    post_install_task_list->AddWorkItem(in_use_update_work_items.release());
1190  }
1191
1192  // Append work items that will be executed if this was NOT an in-use update.
1193  {
1194    scoped_ptr<WorkItemList> regular_update_work_items(
1195        WorkItem::CreateConditionalWorkItemList(
1196            new Not(new ConditionRunIfFileExists(new_chrome_exe))));
1197    regular_update_work_items->set_log_message("RegularUpdateWorkItemList");
1198
1199    // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
1200    for (size_t i = 0; i < products.size(); ++i) {
1201      BrowserDistribution* dist = products[i]->distribution();
1202      base::string16 version_key(dist->GetVersionKey());
1203      regular_update_work_items->AddDeleteRegValueWorkItem(
1204          root,
1205          version_key,
1206          KEY_WOW64_32KEY,
1207          google_update::kRegOldVersionField);
1208      regular_update_work_items->AddDeleteRegValueWorkItem(
1209          root,
1210          version_key,
1211          KEY_WOW64_32KEY,
1212          google_update::kRegCriticalVersionField);
1213      regular_update_work_items->AddDeleteRegValueWorkItem(
1214          root,
1215          version_key,
1216          KEY_WOW64_32KEY,
1217          google_update::kRegRenameCmdField);
1218    }
1219
1220    post_install_task_list->AddWorkItem(regular_update_work_items.release());
1221  }
1222
1223  AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
1224                                       new_version, post_install_task_list);
1225
1226  // If we're told that we're an MSI install, make sure to set the marker
1227  // in the client state key so that future updates do the right thing.
1228  if (installer_state.is_msi()) {
1229    for (size_t i = 0; i < products.size(); ++i) {
1230      const Product* product = products[i];
1231      AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true,
1232                              post_install_task_list);
1233
1234      // We want MSI installs to take over the Add/Remove Programs shortcut.
1235      // Make a best-effort attempt to delete any shortcuts left over from
1236      // previous non-MSI installations for the same type of install (system or
1237      // per user).
1238      if (product->ShouldCreateUninstallEntry()) {
1239        AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product,
1240                                                   temp_path,
1241                                                   post_install_task_list);
1242      }
1243    }
1244  }
1245
1246  return true;
1247}
1248
1249void AddInstallWorkItems(const InstallationState& original_state,
1250                         const InstallerState& installer_state,
1251                         const base::FilePath& setup_path,
1252                         const base::FilePath& archive_path,
1253                         const base::FilePath& src_path,
1254                         const base::FilePath& temp_path,
1255                         const Version* current_version,
1256                         const Version& new_version,
1257                         WorkItemList* install_list) {
1258  DCHECK(install_list);
1259
1260  const base::FilePath& target_path = installer_state.target_path();
1261
1262  // A temp directory that work items need and the actual install directory.
1263  install_list->AddCreateDirWorkItem(temp_path);
1264  install_list->AddCreateDirWorkItem(target_path);
1265
1266  if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ||
1267      installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
1268    AddChromeWorkItems(original_state,
1269                       installer_state,
1270                       setup_path,
1271                       archive_path,
1272                       src_path,
1273                       temp_path,
1274                       current_version,
1275                       new_version,
1276                       install_list);
1277  }
1278
1279  if (installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
1280    install_list->AddCopyTreeWorkItem(
1281        src_path.Append(installer::kChromeAppHostExe).value(),
1282        target_path.Append(installer::kChromeAppHostExe).value(),
1283        temp_path.value(),
1284        WorkItem::ALWAYS,
1285        L"");
1286  }
1287
1288  // Copy installer in install directory
1289  AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path,
1290                        new_version, install_list);
1291
1292  const HKEY root = installer_state.root_key();
1293  // Only set "lang" for user-level installs since for system-level, the install
1294  // language may not be related to a given user's runtime language.
1295  const bool add_language_identifier = !installer_state.system_install();
1296
1297  const Products& products = installer_state.products();
1298  for (Products::const_iterator it = products.begin(); it < products.end();
1299       ++it) {
1300    const Product& product = **it;
1301
1302    AddUninstallShortcutWorkItems(installer_state, setup_path, new_version,
1303                                  product, install_list);
1304
1305    BrowserDistribution* dist = product.distribution();
1306    AddVersionKeyWorkItems(root,
1307                           dist->GetVersionKey(),
1308                           dist->GetDisplayName(),
1309                           new_version,
1310                           add_language_identifier,
1311                           install_list);
1312
1313    AddDelegateExecuteWorkItems(installer_state, target_path, new_version,
1314                                product, install_list);
1315
1316    AddActiveSetupWorkItems(installer_state, setup_path, new_version, product,
1317                            install_list);
1318  }
1319
1320  // TODO(huangs): Implement actual migration code and remove the hack below.
1321  // If installing Chrome without the legacy stand-alone App Launcher (to be
1322  // handled later), add "shadow" App Launcher registry keys so Google Update
1323  // would recognize the "dr" value in the App Launcher ClientState key.
1324  // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome.
1325  if (installer_state.is_multi_install() &&
1326      installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) &&
1327      !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
1328    BrowserDistribution* shadow_app_launcher_dist =
1329        BrowserDistribution::GetSpecificDistribution(
1330            BrowserDistribution::CHROME_APP_HOST);
1331    AddVersionKeyWorkItems(root,
1332                           shadow_app_launcher_dist->GetVersionKey(),
1333                           shadow_app_launcher_dist->GetDisplayName(),
1334                           new_version,
1335                           add_language_identifier,
1336                           install_list);
1337  }
1338
1339  // Add any remaining work items that involve special settings for
1340  // each product.
1341  AddProductSpecificWorkItems(original_state,
1342                              installer_state,
1343                              setup_path,
1344                              new_version,
1345                              current_version == NULL,
1346                              install_list);
1347
1348  // Copy over brand, usagestats, and other values.
1349  AddGoogleUpdateWorkItems(original_state, installer_state, install_list);
1350
1351  // Append the tasks that run after the installation.
1352  AppendPostInstallTasks(installer_state,
1353                         setup_path,
1354                         current_version,
1355                         new_version,
1356                         temp_path,
1357                         install_list);
1358}
1359
1360void AddRegisterComDllWorkItems(const base::FilePath& dll_folder,
1361                                const std::vector<base::FilePath>& dll_list,
1362                                bool system_level,
1363                                bool do_register,
1364                                bool ignore_failures,
1365                                WorkItemList* work_item_list) {
1366  DCHECK(work_item_list);
1367  if (dll_list.empty()) {
1368    VLOG(1) << "No COM DLLs to register";
1369  } else {
1370    std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin());
1371    for (; dll_iter != dll_list.end(); ++dll_iter) {
1372      base::FilePath dll_path = dll_folder.Append(*dll_iter);
1373      WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
1374          dll_path.value(), do_register, !system_level);
1375      DCHECK(work_item);
1376      work_item->set_ignore_failure(ignore_failures);
1377    }
1378  }
1379}
1380
1381void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
1382                             BrowserDistribution* dist,
1383                             bool set,
1384                             WorkItemList* work_item_list) {
1385  DCHECK(work_item_list);
1386  DWORD msi_value = set ? 1 : 0;
1387  WorkItem* set_msi_work_item =
1388      work_item_list->AddSetRegValueWorkItem(installer_state.root_key(),
1389                                             dist->GetStateKey(),
1390                                             KEY_WOW64_32KEY,
1391                                             google_update::kRegMSIField,
1392                                             msi_value,
1393                                             true);
1394  DCHECK(set_msi_work_item);
1395  set_msi_work_item->set_ignore_failure(true);
1396  set_msi_work_item->set_log_message("Could not write MSI marker!");
1397}
1398
1399void AddDelegateExecuteWorkItems(const InstallerState& installer_state,
1400                                 const base::FilePath& target_path,
1401                                 const Version& new_version,
1402                                 const Product& product,
1403                                 WorkItemList* list) {
1404  base::string16 handler_class_uuid;
1405  BrowserDistribution* dist = product.distribution();
1406  if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) {
1407    if (InstallUtil::IsChromeSxSProcess()) {
1408      CleanupBadCanaryDelegateExecuteRegistration(target_path, list);
1409    } else {
1410      VLOG(1) << "No DelegateExecute verb handler processing to do for "
1411              << dist->GetDisplayName();
1412    }
1413    return;
1414  }
1415
1416  HKEY root = installer_state.root_key();
1417  base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\");
1418  delegate_execute_path.append(handler_class_uuid);
1419
1420  // Unconditionally remove registration regardless of whether or not it is
1421  // needed since builds after r132190 included it when it wasn't strictly
1422  // necessary.  Do this removal before adding in the new key to ensure that
1423  // the COM probe/flush below does its job.
1424  AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list);
1425
1426  // Add work items to register the handler iff it is present.
1427  // See also shell_util.cc's GetProgIdEntries.
1428  if (installer_state.operation() != InstallerState::UNINSTALL) {
1429    VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
1430
1431    // Force COM to flush its cache containing the path to the old handler.
1432    list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback,
1433                                         handler_class_uuid));
1434
1435    // The path to the exe (in the version directory).
1436    base::FilePath delegate_execute(target_path);
1437    if (new_version.IsValid())
1438      delegate_execute = delegate_execute.AppendASCII(new_version.GetString());
1439    delegate_execute = delegate_execute.Append(kDelegateExecuteExe);
1440
1441    // Command-line featuring the quoted path to the exe.
1442    base::string16 command(1, L'"');
1443    command.append(delegate_execute.value()).append(1, L'"');
1444
1445    // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
1446    list->AddCreateRegKeyWorkItem(
1447        root, delegate_execute_path, WorkItem::kWow64Default);
1448    list->AddSetRegValueWorkItem(root,
1449                                 delegate_execute_path,
1450                                 WorkItem::kWow64Default,
1451                                 L"",
1452                                 L"CommandExecuteImpl Class",
1453                                 true);
1454    base::string16 subkey(delegate_execute_path);
1455    subkey.append(L"\\LocalServer32");
1456    list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default);
1457    list->AddSetRegValueWorkItem(
1458        root, subkey, WorkItem::kWow64Default, L"", command, true);
1459    list->AddSetRegValueWorkItem(root,
1460                                 subkey,
1461                                 WorkItem::kWow64Default,
1462                                 L"ServerExecutable",
1463                                 delegate_execute.value(),
1464                                 true);
1465
1466    subkey.assign(delegate_execute_path).append(L"\\Programmable");
1467    list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default);
1468  }
1469}
1470
1471void AddActiveSetupWorkItems(const InstallerState& installer_state,
1472                             const base::FilePath& setup_path,
1473                             const Version& new_version,
1474                             const Product& product,
1475                             WorkItemList* list) {
1476  DCHECK(installer_state.operation() != InstallerState::UNINSTALL);
1477  BrowserDistribution* dist = product.distribution();
1478
1479  if (!product.is_chrome() || !installer_state.system_install()) {
1480    const char* install_level =
1481        installer_state.system_install() ? "system" : "user";
1482    VLOG(1) << "No Active Setup processing to do for " << install_level
1483            << "-level " << dist->GetDisplayName();
1484    return;
1485  }
1486  DCHECK(installer_state.RequiresActiveSetup());
1487
1488  const HKEY root = HKEY_LOCAL_MACHINE;
1489  const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist));
1490
1491  VLOG(1) << "Adding registration items for Active Setup.";
1492  list->AddCreateRegKeyWorkItem(
1493      root, active_setup_path, WorkItem::kWow64Default);
1494  list->AddSetRegValueWorkItem(root,
1495                               active_setup_path,
1496                               WorkItem::kWow64Default,
1497                               L"",
1498                               dist->GetDisplayName(),
1499                               true);
1500
1501  base::FilePath active_setup_exe(installer_state.GetInstallerDirectory(
1502      new_version).Append(kActiveSetupExe));
1503  CommandLine cmd(active_setup_exe);
1504  cmd.AppendSwitch(installer::switches::kConfigureUserSettings);
1505  cmd.AppendSwitch(installer::switches::kVerboseLogging);
1506  cmd.AppendSwitch(installer::switches::kSystemLevel);
1507  product.AppendProductFlags(&cmd);
1508  list->AddSetRegValueWorkItem(root,
1509                               active_setup_path,
1510                               WorkItem::kWow64Default,
1511                               L"StubPath",
1512                               cmd.GetCommandLineString(),
1513                               true);
1514
1515  // TODO(grt): http://crbug.com/75152 Write a reference to a localized
1516  // resource.
1517  list->AddSetRegValueWorkItem(root,
1518                               active_setup_path,
1519                               WorkItem::kWow64Default,
1520                               L"Localized Name",
1521                               dist->GetDisplayName(),
1522                               true);
1523
1524  list->AddSetRegValueWorkItem(root,
1525                               active_setup_path,
1526                               WorkItem::kWow64Default,
1527                               L"IsInstalled",
1528                               static_cast<DWORD>(1U),
1529                               true);
1530
1531  list->AddSetRegValueWorkItem(root,
1532                               active_setup_path,
1533                               WorkItem::kWow64Default,
1534                               L"Version",
1535                               kActiveSetupVersion,
1536                               true);
1537}
1538
1539void AddDeleteOldIELowRightsPolicyWorkItems(
1540    const InstallerState& installer_state,
1541    WorkItemList* install_list) {
1542  DCHECK(install_list);
1543
1544  base::string16 key_path;
1545  GetOldIELowRightsElevationPolicyKeyPath(&key_path);
1546  install_list->AddDeleteRegKeyWorkItem(
1547      installer_state.root_key(), key_path, WorkItem::kWow64Default);
1548}
1549
1550void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
1551                                     const Product& product,
1552                                     CommandLine* uninstall_cmd) {
1553  DCHECK(uninstall_cmd);
1554
1555  uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
1556
1557  // Append the product-specific uninstall flags.
1558  product.AppendProductFlags(uninstall_cmd);
1559  if (installer_state.is_msi())
1560    uninstall_cmd->AppendSwitch(installer::switches::kMsi);
1561  if (installer_state.system_install())
1562    uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
1563  if (installer_state.verbose_logging())
1564    uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
1565}
1566
1567void RefreshElevationPolicy() {
1568  const wchar_t kIEFrameDll[] = L"ieframe.dll";
1569  const char kIERefreshPolicy[] = "IERefreshElevationPolicy";
1570
1571  HMODULE ieframe = LoadLibrary(kIEFrameDll);
1572  if (ieframe) {
1573    typedef HRESULT (__stdcall *IERefreshPolicy)();
1574    IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
1575        GetProcAddress(ieframe, kIERefreshPolicy));
1576
1577    if (ie_refresh_policy) {
1578      ie_refresh_policy();
1579    } else {
1580      VLOG(1) << kIERefreshPolicy << " not supported.";
1581    }
1582
1583    FreeLibrary(ieframe);
1584  } else {
1585    VLOG(1) << "Cannot load " << kIEFrameDll;
1586  }
1587}
1588
1589void AddOsUpgradeWorkItems(const InstallerState& installer_state,
1590                           const base::FilePath& setup_path,
1591                           const Version& new_version,
1592                           const Product& product,
1593                           WorkItemList* install_list) {
1594  const HKEY root_key = installer_state.root_key();
1595  base::string16 cmd_key(
1596      GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade));
1597
1598  if (installer_state.operation() == InstallerState::UNINSTALL) {
1599    install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY)
1600        ->set_log_message("Removing OS upgrade command");
1601  } else {
1602    // Register with Google Update to have setup.exe --on-os-upgrade called on
1603    // OS upgrade.
1604    CommandLine cmd_line(installer_state
1605        .GetInstallerDirectory(new_version)
1606        .Append(setup_path.BaseName()));
1607    // Add the main option to indicate OS upgrade flow.
1608    cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
1609    // Add product-specific options.
1610    product.AppendProductFlags(&cmd_line);
1611    if (installer_state.system_install())
1612      cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1613    // Log everything for now.
1614    cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1615
1616    AppCommand cmd(cmd_line.GetCommandLineString());
1617    cmd.set_is_auto_run_on_os_upgrade(true);
1618    cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list);
1619  }
1620}
1621
1622void AddQueryEULAAcceptanceWorkItems(const InstallerState& installer_state,
1623                                     const base::FilePath& setup_path,
1624                                     const Version& new_version,
1625                                     const Product& product,
1626                                     WorkItemList* work_item_list) {
1627  const HKEY root_key = installer_state.root_key();
1628  base::string16 cmd_key(
1629      GetRegCommandKey(product.distribution(), kCmdQueryEULAAcceptance));
1630  if (installer_state.operation() == InstallerState::UNINSTALL) {
1631    work_item_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY)
1632        ->set_log_message("Removing query EULA acceptance command");
1633  } else {
1634    CommandLine cmd_line(installer_state
1635        .GetInstallerDirectory(new_version)
1636        .Append(setup_path.BaseName()));
1637    cmd_line.AppendSwitch(switches::kQueryEULAAcceptance);
1638    if (installer_state.system_install())
1639      cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1640    if (installer_state.verbose_logging())
1641      cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1642    AppCommand cmd(cmd_line.GetCommandLineString());
1643    cmd.set_is_web_accessible(true);
1644    cmd.set_is_run_as_user(true);
1645    cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
1646  }
1647}
1648
1649void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state,
1650                                        WorkItemList* work_item_list) {
1651  DCHECK(work_item_list);
1652
1653  base::string16 cmd_key(
1654      GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
1655                           BrowserDistribution::CHROME_BINARIES),
1656                       kCmdQuickEnableCf));
1657
1658  // Unconditionally remove the legacy Quick Enable command from the binaries.
1659  // Do this even if multi-install Chrome isn't installed to ensure that it is
1660  // not left behind in any case.
1661  work_item_list->AddDeleteRegKeyWorkItem(
1662                      installer_state.root_key(), cmd_key, KEY_WOW64_32KEY)
1663      ->set_log_message("removing " + base::UTF16ToASCII(kCmdQuickEnableCf) +
1664                        " command");
1665}
1666
1667}  // namespace installer
1668