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