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