shell_util.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 defines functions that integrate Chrome in Windows shell. These
6// functions can be used by Chrome as well as Chrome installer. All of the
7// work is done by the local functions defined in anonymous namespace in
8// this class.
9
10#include "chrome/installer/util/shell_util.h"
11
12#include <windows.h>
13#include <shlobj.h>
14
15#include <limits>
16#include <string>
17
18#include "base/command_line.h"
19#include "base/file_util.h"
20#include "base/files/file_path.h"
21#include "base/lazy_instance.h"
22#include "base/logging.h"
23#include "base/md5.h"
24#include "base/memory/scoped_ptr.h"
25#include "base/memory/scoped_vector.h"
26#include "base/path_service.h"
27#include "base/string16.h"
28#include "base/string_util.h"
29#include "base/strings/string_number_conversions.h"
30#include "base/strings/string_split.h"
31#include "base/utf_string_conversions.h"
32#include "base/values.h"
33#include "base/win/registry.h"
34#include "base/win/scoped_co_mem.h"
35#include "base/win/scoped_comptr.h"
36#include "base/win/shortcut.h"
37#include "base/win/win_util.h"
38#include "base/win/windows_version.h"
39#include "chrome/common/chrome_constants.h"
40#include "chrome/common/chrome_switches.h"
41#include "chrome/installer/util/browser_distribution.h"
42#include "chrome/installer/util/install_util.h"
43#include "chrome/installer/util/l10n_string_util.h"
44#include "chrome/installer/util/master_preferences.h"
45#include "chrome/installer/util/master_preferences_constants.h"
46#include "chrome/installer/util/util_constants.h"
47
48#include "installer_util_strings.h"  // NOLINT
49
50using base::win::RegKey;
51
52namespace {
53
54// An enum used to tell QuickIsChromeRegistered() which level of registration
55// the caller wants to confirm.
56enum RegistrationConfirmationLevel {
57  // Only look for Chrome's ProgIds.
58  // This is sufficient when we are trying to determine the suffix of the
59  // currently running Chrome as shell integration registrations might not be
60  // present.
61  CONFIRM_PROGID_REGISTRATION = 0,
62  // Confirm that Chrome is fully integrated with Windows (i.e. registered with
63  // Defaut Programs). These registrations can be in HKCU as of Windows 8.
64  // Note: Shell registration implies ProgId registration.
65  CONFIRM_SHELL_REGISTRATION,
66  // Same as CONFIRM_SHELL_REGISTRATION, but only look in HKLM (used when
67  // uninstalling to know whether elevation is required to clean up the
68  // registry).
69  CONFIRM_SHELL_REGISTRATION_IN_HKLM,
70};
71
72const wchar_t kReinstallCommand[] = L"ReinstallCommand";
73
74// Returns true if Chrome Metro is supported on this OS (Win 8 8370 or greater).
75// TODO(gab): Change this to a simple check for Win 8 once old Win8 builds
76// become irrelevant.
77bool IsChromeMetroSupported() {
78  OSVERSIONINFOEX min_version_info = {};
79  min_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
80  min_version_info.dwMajorVersion = 6;
81  min_version_info.dwMinorVersion = 2;
82  min_version_info.dwBuildNumber = 8370;
83  min_version_info.wServicePackMajor = 0;
84  min_version_info.wServicePackMinor = 0;
85
86  DWORDLONG condition_mask = 0;
87  VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
88  VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
89  VER_SET_CONDITION(condition_mask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
90  VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
91  VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
92
93  DWORD type_mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER |
94      VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
95
96  return VerifyVersionInfo(&min_version_info, type_mask, condition_mask) != 0;
97}
98
99// Returns the current (or installed) browser's ProgId (e.g.
100// "ChromeHTML|suffix|").
101// |suffix| can be the empty string.
102string16 GetBrowserProgId(const string16& suffix) {
103  string16 chrome_html(ShellUtil::kChromeHTMLProgId);
104  chrome_html.append(suffix);
105
106  // ProgIds cannot be longer than 39 characters.
107  // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx.
108  // Make all new registrations comply with this requirement (existing
109  // registrations must be preserved).
110  string16 new_style_suffix;
111  if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) &&
112      suffix == new_style_suffix && chrome_html.length() > 39) {
113    NOTREACHED();
114    chrome_html.erase(39);
115  }
116  return chrome_html;
117}
118
119// This class is used to initialize and cache a base 32 encoding of the md5 hash
120// of this user's sid preceded by a dot.
121// This is guaranteed to be unique on the machine and 27 characters long
122// (including the '.').
123// This is then meant to be used as a suffix on all registrations that may
124// conflict with another user-level Chrome install.
125class UserSpecificRegistrySuffix {
126 public:
127  // All the initialization is done in the constructor to be able to build the
128  // suffix in a thread-safe manner when used in conjunction with a
129  // LazyInstance.
130  UserSpecificRegistrySuffix();
131
132  // Sets |suffix| to the pre-computed suffix cached in this object.
133  // Returns true unless the initialization originally failed.
134  bool GetSuffix(string16* suffix);
135
136 private:
137  string16 suffix_;
138
139  DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix);
140};  // class UserSpecificRegistrySuffix
141
142UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() {
143  string16 user_sid;
144  if (!base::win::GetUserSidString(&user_sid)) {
145    NOTREACHED();
146    return;
147  }
148  COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_);
149  base::MD5Digest md5_digest;
150  std::string user_sid_ascii(UTF16ToASCII(user_sid));
151  base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest);
152  const string16 base32_md5(
153      ShellUtil::ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a)));
154  // The value returned by the base32 algorithm above must never change and
155  // must always be 26 characters long (i.e. if someone ever moves this to
156  // base and implements the full base32 algorithm (i.e. with appended '='
157  // signs in the output), they must provide a flag to allow this method to
158  // still request the output with no appended '=' signs).
159  DCHECK_EQ(base32_md5.length(), 26U);
160  suffix_.reserve(base32_md5.length() + 1);
161  suffix_.assign(1, L'.');
162  suffix_.append(base32_md5);
163}
164
165bool UserSpecificRegistrySuffix::GetSuffix(string16* suffix) {
166  if (suffix_.empty()) {
167    NOTREACHED();
168    return false;
169  }
170  suffix->assign(suffix_);
171  return true;
172}
173
174// This class represents a single registry entry. The objective is to
175// encapsulate all the registry entries required for registering Chrome at one
176// place. This class can not be instantiated outside the class and the objects
177// of this class type can be obtained only by calling a static method of this
178// class.
179class RegistryEntry {
180 public:
181  // A bit-field enum of places to look for this key in the Windows registry.
182  enum LookForIn {
183    LOOK_IN_HKCU = 1 << 0,
184    LOOK_IN_HKLM = 1 << 1,
185    LOOK_IN_HKCU_THEN_HKLM = LOOK_IN_HKCU | LOOK_IN_HKLM,
186  };
187
188  // Returns the Windows browser client registration key for Chrome.  For
189  // example: "Software\Clients\StartMenuInternet\Chromium[.user]".  Strictly
190  // speaking, we should use the name of the executable (e.g., "chrome.exe"),
191  // but that ship has sailed.  The cost of switching now is re-prompting users
192  // to make Chrome their default browser, which isn't polite.  |suffix| is the
193  // user-specific registration suffix; see GetUserSpecificDefaultBrowserSuffix
194  // in shell_util.h for details.
195  static string16 GetBrowserClientKey(BrowserDistribution* dist,
196                                      const string16& suffix) {
197    DCHECK(suffix.empty() || suffix[0] == L'.');
198    return string16(ShellUtil::kRegStartMenuInternet)
199        .append(1, L'\\')
200        .append(dist->GetBaseAppName())
201        .append(suffix);
202  }
203
204  // Returns the Windows Default Programs capabilities key for Chrome.  For
205  // example:
206  // "Software\Clients\StartMenuInternet\Chromium[.user]\Capabilities".
207  static string16 GetCapabilitiesKey(BrowserDistribution* dist,
208                                     const string16& suffix) {
209    return GetBrowserClientKey(dist, suffix).append(L"\\Capabilities");
210  }
211
212  // This method returns a list of all the registry entries that
213  // are needed to register this installation's ProgId and AppId.
214  // These entries need to be registered in HKLM prior to Win8.
215  static void GetProgIdEntries(BrowserDistribution* dist,
216                               const string16& chrome_exe,
217                               const string16& suffix,
218                               ScopedVector<RegistryEntry>* entries) {
219    string16 icon_path(
220        ShellUtil::FormatIconLocation(chrome_exe, dist->GetIconIndex()));
221    string16 open_cmd(ShellUtil::GetChromeShellOpenCmd(chrome_exe));
222    string16 delegate_command(ShellUtil::GetChromeDelegateCommand(chrome_exe));
223    // For user-level installs: entries for the app id and DelegateExecute verb
224    // handler will be in HKCU; thus we do not need a suffix on those entries.
225    string16 app_id(
226        ShellUtil::GetBrowserModelId(
227            dist, InstallUtil::IsPerUserInstall(chrome_exe.c_str())));
228    string16 delegate_guid;
229    bool set_delegate_execute =
230        IsChromeMetroSupported() &&
231        dist->GetCommandExecuteImplClsid(&delegate_guid);
232
233    // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8.
234    if (set_delegate_execute) {
235      string16 model_id_shell(ShellUtil::kRegClasses);
236      model_id_shell.push_back(base::FilePath::kSeparators[0]);
237      model_id_shell.append(app_id);
238      model_id_shell.append(ShellUtil::kRegExePath);
239      model_id_shell.append(ShellUtil::kRegShellPath);
240
241      // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open
242      entries->push_back(new RegistryEntry(model_id_shell,
243                                           ShellUtil::kRegVerbOpen));
244
245      // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is
246      // registered to handle some verbs. This registration has the side-effect
247      // that these verbs now show up in the shortcut's context menu. We
248      // mitigate this side-effect by making the context menu entries
249      // user readable/localized strings. See relevant MSDN article:
250      // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx
251      const struct {
252        const wchar_t* verb;
253        int name_id;
254      } verbs[] = {
255          { ShellUtil::kRegVerbOpen, -1 },
256          { ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE },
257      };
258      for (size_t i = 0; i < arraysize(verbs); ++i) {
259        string16 sub_path(model_id_shell);
260        sub_path.push_back(base::FilePath::kSeparators[0]);
261        sub_path.append(verbs[i].verb);
262
263        // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>
264        if (verbs[i].name_id != -1) {
265          // TODO(grt): http://crbug.com/75152 Write a reference to a localized
266          // resource.
267          string16 verb_name(installer::GetLocalizedString(verbs[i].name_id));
268          entries->push_back(new RegistryEntry(sub_path, verb_name.c_str()));
269        }
270        entries->push_back(new RegistryEntry(
271            sub_path, L"CommandId", L"Browser.Launch"));
272
273        sub_path.push_back(base::FilePath::kSeparators[0]);
274        sub_path.append(ShellUtil::kRegCommand);
275
276        // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command
277        entries->push_back(new RegistryEntry(sub_path, delegate_command));
278        entries->push_back(new RegistryEntry(
279            sub_path, ShellUtil::kRegDelegateExecute, delegate_guid));
280      }
281    }
282
283    // File association ProgId
284    string16 chrome_html_prog_id(ShellUtil::kRegClasses);
285    chrome_html_prog_id.push_back(base::FilePath::kSeparators[0]);
286    chrome_html_prog_id.append(GetBrowserProgId(suffix));
287    entries->push_back(new RegistryEntry(
288        chrome_html_prog_id, ShellUtil::kChromeHTMLProgIdDesc));
289    entries->push_back(new RegistryEntry(
290        chrome_html_prog_id, ShellUtil::kRegUrlProtocol, L""));
291    entries->push_back(new RegistryEntry(
292        chrome_html_prog_id + ShellUtil::kRegDefaultIcon, icon_path));
293    entries->push_back(new RegistryEntry(
294        chrome_html_prog_id + ShellUtil::kRegShellOpen, open_cmd));
295    if (set_delegate_execute) {
296      entries->push_back(new RegistryEntry(
297          chrome_html_prog_id + ShellUtil::kRegShellOpen,
298          ShellUtil::kRegDelegateExecute, delegate_guid));
299    }
300
301    // The following entries are required as of Windows 8, but do not
302    // depend on the DelegateExecute verb handler being set.
303    if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
304      entries->push_back(new RegistryEntry(
305          chrome_html_prog_id, ShellUtil::kRegAppUserModelId, app_id));
306
307      // Add \Software\Classes\ChromeHTML\Application entries
308      string16 chrome_application(chrome_html_prog_id +
309                                  ShellUtil::kRegApplication);
310      entries->push_back(new RegistryEntry(
311          chrome_application, ShellUtil::kRegAppUserModelId, app_id));
312      entries->push_back(new RegistryEntry(
313          chrome_application, ShellUtil::kRegApplicationIcon, icon_path));
314      // TODO(grt): http://crbug.com/75152 Write a reference to a localized
315      // resource for name, description, and company.
316      entries->push_back(new RegistryEntry(
317          chrome_application, ShellUtil::kRegApplicationName,
318          dist->GetAppShortCutName()));
319      entries->push_back(new RegistryEntry(
320          chrome_application, ShellUtil::kRegApplicationDescription,
321          dist->GetAppDescription()));
322      entries->push_back(new RegistryEntry(
323          chrome_application, ShellUtil::kRegApplicationCompany,
324          dist->GetPublisherName()));
325    }
326  }
327
328  // This method returns a list of the registry entries needed to declare a
329  // capability of handling a protocol on Windows.
330  static void GetProtocolCapabilityEntries(
331      BrowserDistribution* dist,
332      const string16& suffix,
333      const string16& protocol,
334      ScopedVector<RegistryEntry>* entries) {
335    entries->push_back(new RegistryEntry(
336        GetCapabilitiesKey(dist, suffix).append(L"\\URLAssociations"),
337        protocol, GetBrowserProgId(suffix)));
338  }
339
340  // This method returns a list of the registry entries required to register
341  // this installation in "RegisteredApplications" on Windows (to appear in
342  // Default Programs, StartMenuInternet, etc.).
343  // These entries need to be registered in HKLM prior to Win8.
344  // If |suffix| is not empty, these entries are guaranteed to be unique on this
345  // machine.
346  static void GetShellIntegrationEntries(BrowserDistribution* dist,
347                                         const string16& chrome_exe,
348                                         const string16& suffix,
349                                         ScopedVector<RegistryEntry>* entries) {
350    const string16 icon_path(
351        ShellUtil::FormatIconLocation(chrome_exe, dist->GetIconIndex()));
352    const string16 quoted_exe_path(L"\"" + chrome_exe + L"\"");
353
354    // Register for the Start Menu "Internet" link (pre-Win7).
355    const string16 start_menu_entry(GetBrowserClientKey(dist, suffix));
356    // Register Chrome's display name.
357    // TODO(grt): http://crbug.com/75152 Also set LocalizedString; see
358    // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144109(v=VS.85).aspx#registering_the_display_name
359    entries->push_back(new RegistryEntry(
360        start_menu_entry, dist->GetAppShortCutName()));
361    // Register the "open" verb for launching Chrome via the "Internet" link.
362    entries->push_back(new RegistryEntry(
363        start_menu_entry + ShellUtil::kRegShellOpen, quoted_exe_path));
364    // Register Chrome's icon for the Start Menu "Internet" link.
365    entries->push_back(new RegistryEntry(
366        start_menu_entry + ShellUtil::kRegDefaultIcon, icon_path));
367
368    // Register installation information.
369    string16 install_info(start_menu_entry + L"\\InstallInfo");
370    // Note: not using CommandLine since it has ambiguous rules for quoting
371    // strings.
372    entries->push_back(new RegistryEntry(install_info, kReinstallCommand,
373        quoted_exe_path + L" --" + ASCIIToWide(switches::kMakeDefaultBrowser)));
374    entries->push_back(new RegistryEntry(install_info, L"HideIconsCommand",
375        quoted_exe_path + L" --" + ASCIIToWide(switches::kHideIcons)));
376    entries->push_back(new RegistryEntry(install_info, L"ShowIconsCommand",
377        quoted_exe_path + L" --" + ASCIIToWide(switches::kShowIcons)));
378    entries->push_back(new RegistryEntry(install_info, L"IconsVisible", 1));
379
380    // Register with Default Programs.
381    const string16 reg_app_name(dist->GetBaseAppName().append(suffix));
382    // Tell Windows where to find Chrome's Default Programs info.
383    const string16 capabilities(GetCapabilitiesKey(dist, suffix));
384    entries->push_back(new RegistryEntry(ShellUtil::kRegRegisteredApplications,
385        reg_app_name, capabilities));
386    // Write out Chrome's Default Programs info.
387    // TODO(grt): http://crbug.com/75152 Write a reference to a localized
388    // resource rather than this.
389    entries->push_back(new RegistryEntry(
390        capabilities, ShellUtil::kRegApplicationDescription,
391        dist->GetLongAppDescription()));
392    entries->push_back(new RegistryEntry(
393        capabilities, ShellUtil::kRegApplicationIcon, icon_path));
394    entries->push_back(new RegistryEntry(
395        capabilities, ShellUtil::kRegApplicationName,
396        dist->GetAppShortCutName()));
397
398    entries->push_back(new RegistryEntry(capabilities + L"\\Startmenu",
399        L"StartMenuInternet", reg_app_name));
400
401    const string16 html_prog_id(GetBrowserProgId(suffix));
402    for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
403      entries->push_back(new RegistryEntry(
404          capabilities + L"\\FileAssociations",
405          ShellUtil::kFileAssociations[i], html_prog_id));
406    }
407    for (int i = 0; ShellUtil::kPotentialProtocolAssociations[i] != NULL;
408        i++) {
409      entries->push_back(new RegistryEntry(
410          capabilities + L"\\URLAssociations",
411          ShellUtil::kPotentialProtocolAssociations[i], html_prog_id));
412    }
413  }
414
415  // This method returns a list of the registry entries required for this
416  // installation to be registered in the Windows shell.
417  // In particular:
418  //  - App Paths
419  //    http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
420  //  - File Associations
421  //    http://msdn.microsoft.com/en-us/library/bb166549
422  // These entries need to be registered in HKLM prior to Win8.
423  static void GetAppRegistrationEntries(const string16& chrome_exe,
424                                        const string16& suffix,
425                                        ScopedVector<RegistryEntry>* entries) {
426    const base::FilePath chrome_path(chrome_exe);
427    string16 app_path_key(ShellUtil::kAppPathsRegistryKey);
428    app_path_key.push_back(base::FilePath::kSeparators[0]);
429    app_path_key.append(chrome_path.BaseName().value());
430    entries->push_back(new RegistryEntry(app_path_key, chrome_exe));
431    entries->push_back(new RegistryEntry(app_path_key,
432        ShellUtil::kAppPathsRegistryPathName, chrome_path.DirName().value()));
433
434    const string16 html_prog_id(GetBrowserProgId(suffix));
435    for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
436      string16 key(ShellUtil::kRegClasses);
437      key.push_back(base::FilePath::kSeparators[0]);
438      key.append(ShellUtil::kFileAssociations[i]);
439      key.push_back(base::FilePath::kSeparators[0]);
440      key.append(ShellUtil::kRegOpenWithProgids);
441      entries->push_back(new RegistryEntry(key, html_prog_id, string16()));
442    }
443  }
444
445  // This method returns a list of all the user level registry entries that
446  // are needed to make Chromium the default handler for a protocol on XP.
447  static void GetXPStyleUserProtocolEntries(
448      const string16& protocol,
449      const string16& chrome_icon,
450      const string16& chrome_open,
451      ScopedVector<RegistryEntry>* entries) {
452    // Protocols associations.
453    string16 url_key(ShellUtil::kRegClasses);
454    url_key.push_back(base::FilePath::kSeparators[0]);
455    url_key.append(protocol);
456
457    // This registry value tells Windows that this 'class' is a URL scheme
458    // so IE, explorer and other apps will route it to our handler.
459    // <root hkey>\Software\Classes\<protocol>\URL Protocol
460    entries->push_back(new RegistryEntry(url_key,
461        ShellUtil::kRegUrlProtocol, L""));
462
463    // <root hkey>\Software\Classes\<protocol>\DefaultIcon
464    string16 icon_key = url_key + ShellUtil::kRegDefaultIcon;
465    entries->push_back(new RegistryEntry(icon_key, chrome_icon));
466
467    // <root hkey>\Software\Classes\<protocol>\shell\open\command
468    string16 shell_key = url_key + ShellUtil::kRegShellOpen;
469    entries->push_back(new RegistryEntry(shell_key, chrome_open));
470
471    // <root hkey>\Software\Classes\<protocol>\shell\open\ddeexec
472    string16 dde_key = url_key + L"\\shell\\open\\ddeexec";
473    entries->push_back(new RegistryEntry(dde_key, L""));
474
475    // <root hkey>\Software\Classes\<protocol>\shell\@
476    string16 protocol_shell_key = url_key + ShellUtil::kRegShellPath;
477    entries->push_back(new RegistryEntry(protocol_shell_key, L"open"));
478  }
479
480  // This method returns a list of all the user level registry entries that
481  // are needed to make Chromium default browser on XP.
482  // Some of these entries are irrelevant in recent versions of Windows, but
483  // we register them anyways as some legacy apps are hardcoded to lookup those
484  // values.
485  static void GetXPStyleDefaultBrowserUserEntries(
486      BrowserDistribution* dist,
487      const string16& chrome_exe,
488      const string16& suffix,
489      ScopedVector<RegistryEntry>* entries) {
490    // File extension associations.
491    string16 html_prog_id(GetBrowserProgId(suffix));
492    for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
493      string16 ext_key(ShellUtil::kRegClasses);
494      ext_key.push_back(base::FilePath::kSeparators[0]);
495      ext_key.append(ShellUtil::kFileAssociations[i]);
496      entries->push_back(new RegistryEntry(ext_key, html_prog_id));
497    }
498
499    // Protocols associations.
500    string16 chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe);
501    string16 chrome_icon =
502        ShellUtil::FormatIconLocation(chrome_exe, dist->GetIconIndex());
503    for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) {
504      GetXPStyleUserProtocolEntries(ShellUtil::kBrowserProtocolAssociations[i],
505                                    chrome_icon, chrome_open, entries);
506    }
507
508    // start->Internet shortcut.
509    string16 start_menu(ShellUtil::kRegStartMenuInternet);
510    string16 app_name = dist->GetBaseAppName() + suffix;
511    entries->push_back(new RegistryEntry(start_menu, app_name));
512  }
513
514  // Generate work_item tasks required to create current registry entry and
515  // add them to the given work item list.
516  void AddToWorkItemList(HKEY root, WorkItemList *items) const {
517    items->AddCreateRegKeyWorkItem(root, _key_path);
518    if (_is_string) {
519      items->AddSetRegValueWorkItem(root, _key_path, _name, _value, true);
520    } else {
521      items->AddSetRegValueWorkItem(root, _key_path, _name, _int_value, true);
522    }
523  }
524
525  // Checks if the current registry entry exists in HKCU\|_key_path|\|_name|
526  // and value is |_value|. If the key does NOT exist in HKCU, checks for
527  // the correct name and value in HKLM.
528  // |look_for_in| specifies roots (HKCU and/or HKLM) in which to look for the
529  // key, unspecified roots are not looked into (i.e. the the key is assumed not
530  // to exist in them).
531  // |look_for_in| must at least specify one root to look into.
532  // If |look_for_in| is LOOK_IN_HKCU_THEN_HKLM, this method mimics Windows'
533  // behavior when searching in HKCR (HKCU takes precedence over HKLM). For
534  // registrations outside of HKCR on versions of Windows prior to Win8,
535  // Chrome's values go in HKLM. This function will make unnecessary (but
536  // harmless) queries into HKCU in that case.
537  bool ExistsInRegistry(uint32 look_for_in) const {
538    DCHECK(look_for_in);
539
540    RegistryStatus status = DOES_NOT_EXIST;
541    if (look_for_in & LOOK_IN_HKCU)
542      status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER);
543    if (status == DOES_NOT_EXIST && (look_for_in & LOOK_IN_HKLM))
544      status = StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE);
545    return status == SAME_VALUE;
546  }
547
548 private:
549  // States this RegistryKey can be in compared to the registry.
550  enum RegistryStatus {
551    // |_name| does not exist in the registry
552    DOES_NOT_EXIST,
553    // |_name| exists, but its value != |_value|
554    DIFFERENT_VALUE,
555    // |_name| exists and its value is |_value|
556    SAME_VALUE,
557  };
558
559  // Create a object that represent default value of a key
560  RegistryEntry(const string16& key_path, const string16& value)
561      : _key_path(key_path), _name(),
562        _is_string(true), _value(value), _int_value(0) {
563  }
564
565  // Create a object that represent a key of type REG_SZ
566  RegistryEntry(const string16& key_path, const string16& name,
567                const string16& value)
568      : _key_path(key_path), _name(name),
569        _is_string(true), _value(value), _int_value(0) {
570  }
571
572  // Create a object that represent a key of integer type
573  RegistryEntry(const string16& key_path, const string16& name,
574                DWORD value)
575      : _key_path(key_path), _name(name),
576        _is_string(false), _value(), _int_value(value) {
577  }
578
579  string16 _key_path;  // key path for the registry entry
580  string16 _name;      // name of the registry entry
581  bool _is_string;     // true if current registry entry is of type REG_SZ
582  string16 _value;     // string value (useful if _is_string = true)
583  DWORD _int_value;    // integer value (useful if _is_string = false)
584
585  // Helper function for ExistsInRegistry().
586  // Returns the RegistryStatus of the current registry entry in
587  // |root|\|_key_path|\|_name|.
588  RegistryStatus StatusInRegistryUnderRoot(HKEY root) const {
589    RegKey key(root, _key_path.c_str(), KEY_QUERY_VALUE);
590    bool found = false;
591    bool correct_value = false;
592    if (_is_string) {
593      string16 read_value;
594      found = key.ReadValue(_name.c_str(), &read_value) == ERROR_SUCCESS;
595      correct_value = read_value.size() == _value.size() &&
596          std::equal(_value.begin(), _value.end(), read_value.begin(),
597                     base::CaseInsensitiveCompare<wchar_t>());
598    } else {
599      DWORD read_value;
600      found = key.ReadValueDW(_name.c_str(), &read_value) == ERROR_SUCCESS;
601      correct_value = read_value == _int_value;
602    }
603    return found ?
604        (correct_value ? SAME_VALUE : DIFFERENT_VALUE) : DOES_NOT_EXIST;
605  }
606
607  DISALLOW_COPY_AND_ASSIGN(RegistryEntry);
608};  // class RegistryEntry
609
610
611// This method converts all the RegistryEntries from the given list to
612// Set/CreateRegWorkItems and runs them using WorkItemList.
613bool AddRegistryEntries(HKEY root, const ScopedVector<RegistryEntry>& entries) {
614  scoped_ptr<WorkItemList> items(WorkItem::CreateWorkItemList());
615
616  for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin();
617       itr != entries.end(); ++itr)
618    (*itr)->AddToWorkItemList(root, items.get());
619
620  // Apply all the registry changes and if there is a problem, rollback
621  if (!items->Do()) {
622    items->Rollback();
623    return false;
624  }
625  return true;
626}
627
628// Checks that all |entries| are present on this computer.
629// |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation
630// for it can be found there.
631bool AreEntriesRegistered(const ScopedVector<RegistryEntry>& entries,
632                          uint32 look_for_in) {
633  bool registered = true;
634  for (ScopedVector<RegistryEntry>::const_iterator itr = entries.begin();
635       registered && itr != entries.end(); ++itr) {
636    // We do not need registered = registered && ... since the loop condition
637    // is set to exit early.
638    registered = (*itr)->ExistsInRegistry(look_for_in);
639  }
640  return registered;
641}
642
643// Checks that all required registry entries for Chrome are already present
644// on this computer. See RegistryEntry::ExistsInRegistry for the behavior of
645// |look_for_in|.
646// Note: between r133333 and r154145 we were registering parts of Chrome in HKCU
647// and parts in HKLM for user-level installs; we now always register everything
648// under a single registry root. Not doing so caused http://crbug.com/144910 for
649// users who first-installed Chrome in that revision range (those users are
650// still impacted by http://crbug.com/144910). This method will keep returning
651// true for affected users (i.e. who have all the registrations, but over both
652// registry roots).
653bool IsChromeRegistered(BrowserDistribution* dist,
654                        const string16& chrome_exe,
655                        const string16& suffix,
656                        uint32 look_for_in) {
657  ScopedVector<RegistryEntry> entries;
658  RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries);
659  RegistryEntry::GetShellIntegrationEntries(dist, chrome_exe, suffix, &entries);
660  RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix, &entries);
661  return AreEntriesRegistered(entries, look_for_in);
662}
663
664// This method checks if Chrome is already registered on the local machine
665// for the requested protocol. It just checks the one value required for this.
666// See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|.
667bool IsChromeRegisteredForProtocol(BrowserDistribution* dist,
668                                   const string16& suffix,
669                                   const string16& protocol,
670                                   uint32 look_for_in) {
671  ScopedVector<RegistryEntry> entries;
672  RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, &entries);
673  return AreEntriesRegistered(entries, look_for_in);
674}
675
676// This method registers Chrome on Vista by launching an elevated setup.exe.
677// That will show the user the standard Vista elevation prompt. If the user
678// accepts it the new process will make the necessary changes and return SUCCESS
679// that we capture and return.
680// If protocol is non-empty we will also register Chrome as being capable of
681// handling the protocol.
682bool ElevateAndRegisterChrome(BrowserDistribution* dist,
683                              const string16& chrome_exe,
684                              const string16& suffix,
685                              const string16& protocol) {
686  // Only user-level installs prior to Windows 8 should need to elevate to
687  // register.
688  DCHECK(InstallUtil::IsPerUserInstall(chrome_exe.c_str()));
689  DCHECK_LT(base::win::GetVersion(), base::win::VERSION_WIN8);
690  base::FilePath exe_path =
691      base::FilePath::FromWStringHack(chrome_exe).DirName()
692          .Append(installer::kSetupExe);
693  if (!file_util::PathExists(exe_path)) {
694    HKEY reg_root = InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ?
695        HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
696    RegKey key(reg_root, dist->GetUninstallRegPath().c_str(), KEY_READ);
697    string16 uninstall_string;
698    key.ReadValue(installer::kUninstallStringField, &uninstall_string);
699    CommandLine command_line = CommandLine::FromString(uninstall_string);
700    exe_path = command_line.GetProgram();
701  }
702
703  if (file_util::PathExists(exe_path)) {
704    CommandLine cmd(exe_path);
705    cmd.AppendSwitchNative(installer::switches::kRegisterChromeBrowser,
706                           chrome_exe);
707    if (!suffix.empty()) {
708      cmd.AppendSwitchNative(
709          installer::switches::kRegisterChromeBrowserSuffix, suffix);
710    }
711
712    CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
713    if (browser_command_line.HasSwitch(switches::kChromeFrame)) {
714      cmd.AppendSwitch(installer::switches::kChromeFrame);
715    }
716
717    if (!protocol.empty()) {
718      cmd.AppendSwitchNative(
719        installer::switches::kRegisterURLProtocol, protocol);
720    }
721
722    DWORD ret_val = 0;
723    InstallUtil::ExecuteExeAsAdmin(cmd, &ret_val);
724    if (ret_val == 0)
725      return true;
726  }
727  return false;
728}
729
730// Launches the Windows 7 and Windows 8 dialog for picking the application to
731// handle the given protocol. Most importantly, this is used to set the default
732// handler for http (and, implicitly with it, https). In that case it is also
733// known as the 'how do you want to open webpages' dialog.
734// It is required that Chrome be already *registered* for the given protocol.
735bool LaunchSelectDefaultProtocolHandlerDialog(const wchar_t* protocol) {
736  DCHECK(protocol);
737  OPENASINFO open_as_info = {};
738  open_as_info.pcszFile = protocol;
739  open_as_info.oaifInFlags =
740      OAIF_URL_PROTOCOL | OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT;
741  HRESULT hr = SHOpenWithDialog(NULL, &open_as_info);
742  DLOG_IF(WARNING, FAILED(hr)) << "Failed to set as default " << protocol
743      << " handler; hr=0x" << std::hex << hr;
744  if (FAILED(hr))
745    return false;
746  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
747  return true;
748}
749
750// Launches the Windows 7 and Windows 8 application association dialog, which
751// is the only documented way to make a browser the default browser on
752// Windows 8.
753bool LaunchApplicationAssociationDialog(const string16& app_id) {
754  base::win::ScopedComPtr<IApplicationAssociationRegistrationUI> aarui;
755  HRESULT hr = aarui.CreateInstance(CLSID_ApplicationAssociationRegistrationUI);
756  if (FAILED(hr))
757    return false;
758  hr = aarui->LaunchAdvancedAssociationUI(app_id.c_str());
759  return SUCCEEDED(hr);
760}
761
762// Returns true if the current install's |chrome_exe| has been registered with
763// |suffix|.
764// |confirmation_level| is the level of verification desired as described in
765// the RegistrationConfirmationLevel enum above.
766// |suffix| can be the empty string (this is used to support old installs
767// where we used to not suffix user-level installs if they were the first to
768// request the non-suffixed registry entries on the machine).
769// NOTE: This a quick check that only validates that a single registry entry
770// points to |chrome_exe|. This should only be used at run-time to determine
771// how Chrome is registered, not to know whether the registration is complete
772// at install-time (IsChromeRegistered() can be used for that).
773bool QuickIsChromeRegistered(BrowserDistribution* dist,
774                             const string16& chrome_exe,
775                             const string16& suffix,
776                             RegistrationConfirmationLevel confirmation_level) {
777  // Get the appropriate key to look for based on the level desired.
778  string16 reg_key;
779  switch (confirmation_level) {
780    case CONFIRM_PROGID_REGISTRATION:
781      // Software\Classes\ChromeHTML|suffix|
782      reg_key = ShellUtil::kRegClasses;
783      reg_key.push_back(base::FilePath::kSeparators[0]);
784      reg_key.append(ShellUtil::kChromeHTMLProgId);
785      reg_key.append(suffix);
786      break;
787    case CONFIRM_SHELL_REGISTRATION:
788    case CONFIRM_SHELL_REGISTRATION_IN_HKLM:
789      // Software\Clients\StartMenuInternet\Google Chrome|suffix|
790      reg_key = RegistryEntry::GetBrowserClientKey(dist, suffix);
791      break;
792    default:
793      NOTREACHED();
794      break;
795  }
796  reg_key.append(ShellUtil::kRegShellOpen);
797
798  // ProgId registrations are allowed to reside in HKCU for user-level installs
799  // (and values there have priority over values in HKLM). The same is true for
800  // shell integration entries as of Windows 8.
801  if (confirmation_level == CONFIRM_PROGID_REGISTRATION ||
802      (confirmation_level == CONFIRM_SHELL_REGISTRATION &&
803       base::win::GetVersion() >= base::win::VERSION_WIN8)) {
804    const RegKey key_hkcu(HKEY_CURRENT_USER, reg_key.c_str(), KEY_QUERY_VALUE);
805    string16 hkcu_value;
806    // If |reg_key| is present in HKCU, assert that it points to |chrome_exe|.
807    // Otherwise, fall back on an HKLM lookup below.
808    if (key_hkcu.ReadValue(L"", &hkcu_value) == ERROR_SUCCESS) {
809      return InstallUtil::ProgramCompare(
810          base::FilePath(chrome_exe)).Evaluate(hkcu_value);
811    }
812  }
813
814  // Assert that |reg_key| points to |chrome_exe| in HKLM.
815  const RegKey key_hklm(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_QUERY_VALUE);
816  string16 hklm_value;
817  if (key_hklm.ReadValue(L"", &hklm_value) == ERROR_SUCCESS) {
818    return InstallUtil::ProgramCompare(
819        base::FilePath(chrome_exe)).Evaluate(hklm_value);
820  }
821  return false;
822}
823
824// Sets |suffix| to a 27 character string that is specific to this user on this
825// machine (on user-level installs only).
826// To support old-style user-level installs however, |suffix| is cleared if the
827// user currently owns the non-suffixed HKLM registrations.
828// |suffix| can also be set to the user's username if the current install is
829// suffixed as per the old-style registrations.
830// |suffix| is cleared on system-level installs.
831// |suffix| should then be appended to all Chrome properties that may conflict
832// with other Chrome user-level installs.
833// Returns true unless one of the underlying calls fails.
834bool GetInstallationSpecificSuffix(BrowserDistribution* dist,
835                                   const string16& chrome_exe,
836                                   string16* suffix) {
837  if (!InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ||
838      QuickIsChromeRegistered(dist, chrome_exe, string16(),
839                              CONFIRM_SHELL_REGISTRATION)) {
840    // No suffix on system-level installs and user-level installs already
841    // registered with no suffix.
842    suffix->clear();
843    return true;
844  }
845
846  // Get the old suffix for the check below.
847  if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix)) {
848    NOTREACHED();
849    return false;
850  }
851  if (QuickIsChromeRegistered(dist, chrome_exe, *suffix,
852                              CONFIRM_SHELL_REGISTRATION)) {
853    // Username suffix for installs that are suffixed as per the old-style.
854    return true;
855  }
856
857  return ShellUtil::GetUserSpecificRegistrySuffix(suffix);
858}
859
860// Returns the root registry key (HKLM or HKCU) under which registrations must
861// be placed for this install. As of Windows 8 everything can go in HKCU for
862// per-user installs.
863HKEY DetermineRegistrationRoot(bool is_per_user) {
864  return is_per_user && base::win::GetVersion() >= base::win::VERSION_WIN8 ?
865      HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
866}
867
868// Associates Chrome with supported protocols and file associations. This should
869// not be required on Vista+ but since some applications still read
870// Software\Classes\http key directly, we have to do this on Vista+ as well.
871bool RegisterChromeAsDefaultXPStyle(BrowserDistribution* dist,
872                                    int shell_change,
873                                    const string16& chrome_exe) {
874  bool ret = true;
875  ScopedVector<RegistryEntry> entries;
876  RegistryEntry::GetXPStyleDefaultBrowserUserEntries(
877      dist, chrome_exe,
878      ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe), &entries);
879
880  // Change the default browser for current user.
881  if ((shell_change & ShellUtil::CURRENT_USER) &&
882      !AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
883    ret = false;
884    LOG(ERROR) << "Could not make Chrome default browser (XP/current user).";
885  }
886
887  // Chrome as default browser at system level.
888  if ((shell_change & ShellUtil::SYSTEM_LEVEL) &&
889      !AddRegistryEntries(HKEY_LOCAL_MACHINE, entries)) {
890    ret = false;
891    LOG(ERROR) << "Could not make Chrome default browser (XP/system level).";
892  }
893
894  return ret;
895}
896
897// Associates Chrome with |protocol| in the registry. This should not be
898// required on Vista+ but since some applications still read these registry
899// keys directly, we have to do this on Vista+ as well.
900// See http://msdn.microsoft.com/library/aa767914.aspx for more details.
901bool RegisterChromeAsDefaultProtocolClientXPStyle(BrowserDistribution* dist,
902                                                  const string16& chrome_exe,
903                                                  const string16& protocol) {
904  ScopedVector<RegistryEntry> entries;
905  const string16 chrome_open(ShellUtil::GetChromeShellOpenCmd(chrome_exe));
906  const string16 chrome_icon(
907      ShellUtil::FormatIconLocation(chrome_exe, dist->GetIconIndex()));
908  RegistryEntry::GetXPStyleUserProtocolEntries(protocol, chrome_icon,
909                                               chrome_open, &entries);
910  // Change the default protocol handler for current user.
911  if (!AddRegistryEntries(HKEY_CURRENT_USER, entries)) {
912    LOG(ERROR) << "Could not make Chrome default protocol client (XP).";
913    return false;
914  }
915
916  return true;
917}
918
919// Returns |properties.shortcut_name| if the property is set, otherwise it
920// returns dist->GetAppShortcutName(). In any case, it makes sure the
921// return value is suffixed with ".lnk".
922string16 ExtractShortcutNameFromProperties(
923    BrowserDistribution* dist,
924    const ShellUtil::ShortcutProperties& properties) {
925  DCHECK(dist);
926  string16 shortcut_name;
927  if (properties.has_shortcut_name())
928    shortcut_name = properties.shortcut_name;
929  else
930    shortcut_name = dist->GetAppShortCutName();
931
932  if (!EndsWith(shortcut_name, installer::kLnkExt, false))
933    shortcut_name.append(installer::kLnkExt);
934
935  return shortcut_name;
936}
937
938// Converts ShellUtil::ShortcutOperation to the best-matching value in
939// base::win::ShortcutOperation.
940base::win::ShortcutOperation TranslateShortcutOperation(
941    ShellUtil::ShortcutOperation operation) {
942  switch (operation) {
943    case ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS:  // Falls through.
944    case ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL:
945      return base::win::SHORTCUT_CREATE_ALWAYS;
946
947    case ShellUtil::SHELL_SHORTCUT_UPDATE_EXISTING:
948      return base::win::SHORTCUT_UPDATE_EXISTING;
949
950    case ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING:
951      return base::win::SHORTCUT_REPLACE_EXISTING;
952
953    default:
954      NOTREACHED();
955      return base::win::SHORTCUT_REPLACE_EXISTING;
956  }
957}
958
959// Returns a base::win::ShortcutProperties struct containing the properties
960// to set on the shortcut based on the provided ShellUtil::ShortcutProperties.
961base::win::ShortcutProperties TranslateShortcutProperties(
962    const ShellUtil::ShortcutProperties& properties) {
963  base::win::ShortcutProperties shortcut_properties;
964
965  if (properties.has_target()) {
966    shortcut_properties.set_target(properties.target);
967    DCHECK(!properties.target.DirName().empty());
968    shortcut_properties.set_working_dir(properties.target.DirName());
969  }
970
971  if (properties.has_arguments())
972    shortcut_properties.set_arguments(properties.arguments);
973
974  if (properties.has_description())
975    shortcut_properties.set_description(properties.description);
976
977  if (properties.has_icon())
978    shortcut_properties.set_icon(properties.icon, properties.icon_index);
979
980  if (properties.has_app_id())
981    shortcut_properties.set_app_id(properties.app_id);
982
983  if (properties.has_dual_mode())
984    shortcut_properties.set_dual_mode(properties.dual_mode);
985
986  return shortcut_properties;
987}
988
989// Cleans up an old verb (run) we used to register in
990// <root>\Software\Classes\Chrome<.suffix>\.exe\shell\run on Windows 8.
991void RemoveRunVerbOnWindows8(
992    BrowserDistribution* dist,
993    const string16& chrome_exe) {
994  if (IsChromeMetroSupported()) {
995    bool is_per_user_install =InstallUtil::IsPerUserInstall(chrome_exe.c_str());
996    HKEY root_key = DetermineRegistrationRoot(is_per_user_install);
997    // There's no need to rollback, so forgo the usual work item lists and just
998    // remove the key from the registry.
999    string16 run_verb_key(ShellUtil::kRegClasses);
1000    run_verb_key.push_back(base::FilePath::kSeparators[0]);
1001    run_verb_key.append(ShellUtil::GetBrowserModelId(
1002        dist, is_per_user_install));
1003    run_verb_key.append(ShellUtil::kRegExePath);
1004    run_verb_key.append(ShellUtil::kRegShellPath);
1005    run_verb_key.push_back(base::FilePath::kSeparators[0]);
1006    run_verb_key.append(ShellUtil::kRegVerbRun);
1007    InstallUtil::DeleteRegistryKey(root_key, run_verb_key);
1008  }
1009}
1010
1011// Gets the short (8.3) form of |path|, putting the result in |short_path| and
1012// returning true on success.  |short_path| is not modified on failure.
1013bool ShortNameFromPath(const base::FilePath& path, string16* short_path) {
1014  DCHECK(short_path);
1015  string16 result(MAX_PATH, L'\0');
1016  DWORD short_length = GetShortPathName(path.value().c_str(), &result[0],
1017                                        result.size());
1018  if (short_length == 0 || short_length > result.size()) {
1019    PLOG(ERROR) << "Error getting short (8.3) path";
1020    return false;
1021  }
1022
1023  result.resize(short_length);
1024  short_path->swap(result);
1025  return true;
1026}
1027
1028// Probe using IApplicationAssociationRegistration::QueryCurrentDefault
1029// (Windows 8); see ProbeProtocolHandlers.  This mechanism is not suitable for
1030// use on previous versions of Windows despite the presence of
1031// QueryCurrentDefault on them since versions of Windows prior to Windows 8
1032// did not perform validation on the ProgID registered as the current default.
1033// As a result, stale ProgIDs could be returned, leading to false positives.
1034ShellUtil::DefaultState ProbeCurrentDefaultHandlers(
1035    const wchar_t* const* protocols,
1036    size_t num_protocols) {
1037  base::win::ScopedComPtr<IApplicationAssociationRegistration> registration;
1038  HRESULT hr = registration.CreateInstance(
1039      CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC);
1040  if (FAILED(hr))
1041    return ShellUtil::UNKNOWN_DEFAULT;
1042
1043  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1044  base::FilePath chrome_exe;
1045  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
1046    NOTREACHED();
1047    return ShellUtil::UNKNOWN_DEFAULT;
1048  }
1049  string16 prog_id(ShellUtil::kChromeHTMLProgId);
1050  prog_id += ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe.value());
1051
1052  for (size_t i = 0; i < num_protocols; ++i) {
1053    base::win::ScopedCoMem<wchar_t> current_app;
1054    hr = registration->QueryCurrentDefault(protocols[i], AT_URLPROTOCOL,
1055                                           AL_EFFECTIVE, &current_app);
1056    if (FAILED(hr) || prog_id.compare(current_app) != 0)
1057      return ShellUtil::NOT_DEFAULT;
1058  }
1059
1060  return ShellUtil::IS_DEFAULT;
1061}
1062
1063// Probe using IApplicationAssociationRegistration::QueryAppIsDefault (Vista and
1064// Windows 7); see ProbeProtocolHandlers.
1065ShellUtil::DefaultState ProbeAppIsDefaultHandlers(
1066    const wchar_t* const* protocols,
1067    size_t num_protocols) {
1068  base::win::ScopedComPtr<IApplicationAssociationRegistration> registration;
1069  HRESULT hr = registration.CreateInstance(
1070      CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC);
1071  if (FAILED(hr))
1072    return ShellUtil::UNKNOWN_DEFAULT;
1073
1074  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1075  base::FilePath chrome_exe;
1076  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
1077    NOTREACHED();
1078    return ShellUtil::UNKNOWN_DEFAULT;
1079  }
1080  string16 app_name(ShellUtil::GetApplicationName(dist, chrome_exe.value()));
1081
1082  BOOL result;
1083  for (size_t i = 0; i < num_protocols; ++i) {
1084    result = TRUE;
1085    hr = registration->QueryAppIsDefault(protocols[i], AT_URLPROTOCOL,
1086        AL_EFFECTIVE, app_name.c_str(), &result);
1087    if (FAILED(hr) || result == FALSE)
1088      return ShellUtil::NOT_DEFAULT;
1089  }
1090
1091  return ShellUtil::IS_DEFAULT;
1092}
1093
1094// Probe the current commands registered to handle the shell "open" verb for
1095// |protocols| (Windows XP); see ProbeProtocolHandlers.
1096ShellUtil::DefaultState ProbeOpenCommandHandlers(
1097    const wchar_t* const* protocols,
1098    size_t num_protocols) {
1099  // Get the path to the current exe (Chrome).
1100  base::FilePath app_path;
1101  if (!PathService::Get(base::FILE_EXE, &app_path)) {
1102    LOG(ERROR) << "Error getting app exe path";
1103    return ShellUtil::UNKNOWN_DEFAULT;
1104  }
1105
1106  // Get its short (8.3) form.
1107  string16 short_app_path;
1108  if (!ShortNameFromPath(app_path, &short_app_path))
1109    return ShellUtil::UNKNOWN_DEFAULT;
1110
1111  const HKEY root_key = HKEY_CLASSES_ROOT;
1112  string16 key_path;
1113  base::win::RegKey key;
1114  string16 value;
1115  CommandLine command_line(CommandLine::NO_PROGRAM);
1116  string16 short_path;
1117
1118  for (size_t i = 0; i < num_protocols; ++i) {
1119    // Get the command line from HKCU\<protocol>\shell\open\command.
1120    key_path.assign(protocols[i]).append(ShellUtil::kRegShellOpen);
1121    if ((key.Open(root_key, key_path.c_str(),
1122                  KEY_QUERY_VALUE) != ERROR_SUCCESS) ||
1123        (key.ReadValue(L"", &value) != ERROR_SUCCESS)) {
1124      return ShellUtil::NOT_DEFAULT;
1125    }
1126
1127    // Need to normalize path in case it's been munged.
1128    command_line = CommandLine::FromString(value);
1129    if (!ShortNameFromPath(command_line.GetProgram(), &short_path))
1130      return ShellUtil::UNKNOWN_DEFAULT;
1131
1132    if (!base::FilePath::CompareEqualIgnoreCase(short_path, short_app_path))
1133      return ShellUtil::NOT_DEFAULT;
1134  }
1135
1136  return ShellUtil::IS_DEFAULT;
1137}
1138
1139// A helper function that probes default protocol handler registration (in a
1140// manner appropriate for the current version of Windows) to determine if
1141// Chrome is the default handler for |protocols|.  Returns IS_DEFAULT
1142// only if Chrome is the default for all specified protocols.
1143ShellUtil::DefaultState ProbeProtocolHandlers(
1144    const wchar_t* const* protocols,
1145    size_t num_protocols) {
1146  DCHECK(!num_protocols || protocols);
1147  if (DCHECK_IS_ON()) {
1148    for (size_t i = 0; i < num_protocols; ++i)
1149      DCHECK(protocols[i] && *protocols[i]);
1150  }
1151
1152  const base::win::Version windows_version = base::win::GetVersion();
1153
1154  if (windows_version >= base::win::VERSION_WIN8)
1155    return ProbeCurrentDefaultHandlers(protocols, num_protocols);
1156  else if (windows_version >= base::win::VERSION_VISTA)
1157    return ProbeAppIsDefaultHandlers(protocols, num_protocols);
1158
1159  return ProbeOpenCommandHandlers(protocols, num_protocols);
1160}
1161
1162// Removes shortcut at |shortcut_path| if it is a shortcut that points to
1163// |target_exe|. If |delete_folder| is true, deletes the parent folder of
1164// the shortcut completely. Returns true if either the shortcut was deleted
1165// successfully or if the shortcut did not point to |target_exe|.
1166bool MaybeRemoveShortcutAtPath(const base::FilePath& shortcut_path,
1167                               const base::FilePath& target_exe,
1168                               bool delete_folder) {
1169  base::FilePath target_path;
1170  if (!base::win::ResolveShortcut(shortcut_path, &target_path, NULL))
1171    return false;
1172
1173  if (InstallUtil::ProgramCompare(target_exe).EvaluatePath(target_path)) {
1174    // Unpin the shortcut if it was ever pinned by the user or the installer.
1175    VLOG(1) << "Trying to unpin " << shortcut_path.value();
1176    if (!base::win::TaskbarUnpinShortcutLink(shortcut_path.value().c_str())) {
1177      VLOG(1) << shortcut_path.value()
1178              << " wasn't pinned (or the unpin failed).";
1179    }
1180    if (delete_folder)
1181      return file_util::Delete(shortcut_path.DirName(), true);
1182    else
1183      return file_util::Delete(shortcut_path, false);
1184  }
1185
1186  // The shortcut at |shortcut_path| doesn't point to |target_exe|, act as if
1187  // our shortcut had been deleted.
1188  return true;
1189}
1190
1191}  // namespace
1192
1193const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon";
1194const wchar_t* ShellUtil::kRegShellPath = L"\\shell";
1195const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command";
1196const wchar_t* ShellUtil::kRegStartMenuInternet =
1197    L"Software\\Clients\\StartMenuInternet";
1198const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes";
1199const wchar_t* ShellUtil::kRegRegisteredApplications =
1200    L"Software\\RegisteredApplications";
1201const wchar_t* ShellUtil::kRegVistaUrlPrefs =
1202    L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
1203    L"http\\UserChoice";
1204const wchar_t* ShellUtil::kAppPathsRegistryKey =
1205    L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
1206const wchar_t* ShellUtil::kAppPathsRegistryPathName = L"Path";
1207
1208#if defined(GOOGLE_CHROME_BUILD)
1209const wchar_t* ShellUtil::kChromeHTMLProgId = L"ChromeHTML";
1210const wchar_t* ShellUtil::kChromeHTMLProgIdDesc = L"Chrome HTML Document";
1211#else
1212// This used to be "ChromiumHTML", but was forced to become "ChromiumHTM"
1213// because of http://crbug.com/153349 as with the '.' and 26 characters suffix
1214// added on user-level installs, the generated progid for Chromium was 39
1215// characters long which, according to MSDN (
1216// http://msdn.microsoft.com/library/aa911706.aspx), is the maximum length
1217// for a progid. It was however determined through experimentation that the 39
1218// character limit mentioned on MSDN includes the NULL character...
1219const wchar_t* ShellUtil::kChromeHTMLProgId = L"ChromiumHTM";
1220const wchar_t* ShellUtil::kChromeHTMLProgIdDesc = L"Chromium HTML Document";
1221#endif
1222
1223const wchar_t* ShellUtil::kFileAssociations[] = {L".htm", L".html", L".shtml",
1224    L".xht", L".xhtml", NULL};
1225const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"ftp", L"http",
1226    L"https", NULL};
1227const wchar_t* ShellUtil::kPotentialProtocolAssociations[] = {L"ftp", L"http",
1228    L"https", L"irc", L"mailto", L"mms", L"news", L"nntp", L"sms", L"smsto",
1229    L"tel", L"urn", L"webcal", NULL};
1230const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol";
1231const wchar_t* ShellUtil::kRegApplication = L"\\Application";
1232const wchar_t* ShellUtil::kRegAppUserModelId = L"AppUserModelId";
1233const wchar_t* ShellUtil::kRegApplicationDescription =
1234    L"ApplicationDescription";
1235const wchar_t* ShellUtil::kRegApplicationName = L"ApplicationName";
1236const wchar_t* ShellUtil::kRegApplicationIcon = L"ApplicationIcon";
1237const wchar_t* ShellUtil::kRegApplicationCompany = L"ApplicationCompany";
1238const wchar_t* ShellUtil::kRegExePath = L"\\.exe";
1239const wchar_t* ShellUtil::kRegVerbOpen = L"open";
1240const wchar_t* ShellUtil::kRegVerbOpenNewWindow = L"opennewwindow";
1241const wchar_t* ShellUtil::kRegVerbRun = L"run";
1242const wchar_t* ShellUtil::kRegCommand = L"command";
1243const wchar_t* ShellUtil::kRegDelegateExecute = L"DelegateExecute";
1244const wchar_t* ShellUtil::kRegOpenWithProgids = L"OpenWithProgids";
1245
1246bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution* dist,
1247                                              const string16& chrome_exe,
1248                                              const string16& suffix) {
1249  return QuickIsChromeRegistered(dist, chrome_exe, suffix,
1250                                 CONFIRM_SHELL_REGISTRATION_IN_HKLM);
1251}
1252
1253bool ShellUtil::GetShortcutPath(ShellUtil::ShortcutLocation location,
1254                                BrowserDistribution* dist,
1255                                ShellChange level,
1256                                base::FilePath* path) {
1257  int dir_key = -1;
1258  bool add_folder_for_dist = false;
1259  switch (location) {
1260    case SHORTCUT_LOCATION_DESKTOP:
1261      dir_key = (level == CURRENT_USER) ? base::DIR_USER_DESKTOP :
1262                                          base::DIR_COMMON_DESKTOP;
1263      break;
1264    case SHORTCUT_LOCATION_QUICK_LAUNCH:
1265      dir_key = (level == CURRENT_USER) ? base::DIR_USER_QUICK_LAUNCH :
1266                                          base::DIR_DEFAULT_USER_QUICK_LAUNCH;
1267      break;
1268    case SHORTCUT_LOCATION_START_MENU:
1269      dir_key = (level == CURRENT_USER) ? base::DIR_START_MENU :
1270                                          base::DIR_COMMON_START_MENU;
1271      add_folder_for_dist = true;
1272      break;
1273    default:
1274      NOTREACHED();
1275      return false;
1276  }
1277
1278  if (!PathService::Get(dir_key, path) || path->empty()) {
1279    NOTREACHED() << dir_key;
1280    return false;
1281  }
1282
1283  if (add_folder_for_dist)
1284    *path = path->Append(dist->GetAppShortCutName());
1285
1286  return true;
1287}
1288
1289bool ShellUtil::CreateOrUpdateShortcut(
1290    ShellUtil::ShortcutLocation location,
1291    BrowserDistribution* dist,
1292    const ShellUtil::ShortcutProperties& properties,
1293    ShellUtil::ShortcutOperation operation) {
1294  DCHECK(dist);
1295  // |pin_to_taskbar| is only acknowledged when first creating the shortcut.
1296  DCHECK(!properties.pin_to_taskbar ||
1297         operation == SHELL_SHORTCUT_CREATE_ALWAYS ||
1298         operation == SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL);
1299
1300  base::FilePath user_shortcut_path;
1301  base::FilePath system_shortcut_path;
1302  if (!GetShortcutPath(location, dist, SYSTEM_LEVEL, &system_shortcut_path) ||
1303      system_shortcut_path.empty()) {
1304    NOTREACHED();
1305    return false;
1306  }
1307
1308  string16 shortcut_name(ExtractShortcutNameFromProperties(dist, properties));
1309  system_shortcut_path = system_shortcut_path.Append(shortcut_name);
1310
1311  base::FilePath* chosen_path;
1312  bool should_install_shortcut = true;
1313  if (properties.level == SYSTEM_LEVEL) {
1314    // Install the system-level shortcut if requested.
1315    chosen_path = &system_shortcut_path;
1316  } else if (operation != SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL ||
1317             !file_util::PathExists(system_shortcut_path)) {
1318    // Otherwise install the user-level shortcut, unless the system-level
1319    // variant of this shortcut is present on the machine and |operation| states
1320    // not to create a user-level shortcut in that case.
1321    if (!GetShortcutPath(location, dist, CURRENT_USER, &user_shortcut_path) ||
1322        user_shortcut_path.empty()) {
1323      NOTREACHED();
1324      return false;
1325    }
1326    user_shortcut_path = user_shortcut_path.Append(shortcut_name);
1327    chosen_path = &user_shortcut_path;
1328  } else {
1329    // Do not install any shortcut if we are told to install a user-level
1330    // shortcut, but the system-level variant of that shortcut is present.
1331    // Other actions (e.g., pinning) can still happen with respect to the
1332    // existing system-level shortcut however.
1333    chosen_path = &system_shortcut_path;
1334    should_install_shortcut = false;
1335  }
1336
1337  if (chosen_path == NULL || chosen_path->empty()) {
1338    NOTREACHED();
1339    return false;
1340  }
1341
1342  base::win::ShortcutOperation shortcut_operation =
1343      TranslateShortcutOperation(operation);
1344  bool ret = true;
1345  if (should_install_shortcut) {
1346    // Make sure the parent directories exist when creating the shortcut.
1347    if (shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS &&
1348        !file_util::CreateDirectory(chosen_path->DirName())) {
1349      NOTREACHED();
1350      return false;
1351    }
1352
1353    base::win::ShortcutProperties shortcut_properties(
1354        TranslateShortcutProperties(properties));
1355    ret = base::win::CreateOrUpdateShortcutLink(
1356        *chosen_path, shortcut_properties, shortcut_operation);
1357  }
1358
1359  if (ret && shortcut_operation == base::win::SHORTCUT_CREATE_ALWAYS &&
1360      properties.pin_to_taskbar &&
1361      base::win::GetVersion() >= base::win::VERSION_WIN7) {
1362    ret = base::win::TaskbarPinShortcutLink(chosen_path->value().c_str());
1363    if (!ret) {
1364      LOG(ERROR) << "Failed to pin " << chosen_path->value();
1365    }
1366  }
1367
1368  return ret;
1369}
1370
1371string16 ShellUtil::FormatIconLocation(const string16& icon_path,
1372                                       int icon_index) {
1373  string16 icon_string(icon_path);
1374  icon_string.append(L",");
1375  icon_string.append(base::IntToString16(icon_index));
1376  return icon_string;
1377}
1378
1379string16 ShellUtil::GetChromeShellOpenCmd(const string16& chrome_exe) {
1380  return L"\"" + chrome_exe + L"\" -- \"%1\"";
1381}
1382
1383string16 ShellUtil::GetChromeDelegateCommand(const string16& chrome_exe) {
1384  return L"\"" + chrome_exe + L"\" -- %*";
1385}
1386
1387void ShellUtil::GetRegisteredBrowsers(
1388    BrowserDistribution* dist,
1389    std::map<string16, string16>* browsers) {
1390  DCHECK(dist);
1391  DCHECK(browsers);
1392
1393  const string16 base_key(ShellUtil::kRegStartMenuInternet);
1394  string16 client_path;
1395  RegKey key;
1396  string16 name;
1397  string16 command;
1398
1399  // HKCU has precedence over HKLM for these registrations: http://goo.gl/xjczJ.
1400  // Look in HKCU second to override any identical values found in HKLM.
1401  const HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
1402  for (int i = 0; i < arraysize(roots); ++i) {
1403    const HKEY root = roots[i];
1404    for (base::win::RegistryKeyIterator iter(root, base_key.c_str());
1405         iter.Valid(); ++iter) {
1406      client_path.assign(base_key).append(1, L'\\').append(iter.Name());
1407      // Read the browser's name (localized according to install language).
1408      if (key.Open(root, client_path.c_str(),
1409                   KEY_QUERY_VALUE) != ERROR_SUCCESS ||
1410          key.ReadValue(NULL, &name) != ERROR_SUCCESS ||
1411          name.empty() ||
1412          name.find(dist->GetBaseAppName()) != string16::npos) {
1413        continue;
1414      }
1415      // Read the browser's reinstall command.
1416      if (key.Open(root, (client_path + L"\\InstallInfo").c_str(),
1417                   KEY_QUERY_VALUE) == ERROR_SUCCESS &&
1418          key.ReadValue(kReinstallCommand, &command) == ERROR_SUCCESS &&
1419          !command.empty()) {
1420        (*browsers)[name] = command;
1421      }
1422    }
1423  }
1424}
1425
1426string16 ShellUtil::GetCurrentInstallationSuffix(BrowserDistribution* dist,
1427                                                 const string16& chrome_exe) {
1428  // This method is somewhat the opposite of GetInstallationSpecificSuffix().
1429  // In this case we are not trying to determine the current suffix for the
1430  // upcoming installation (i.e. not trying to stick to a currently bad
1431  // registration style if one is present).
1432  // Here we want to determine which suffix we should use at run-time.
1433  // In order of preference, we prefer (for user-level installs):
1434  //   1) Base 32 encoding of the md5 hash of the user's sid (new-style).
1435  //   2) Username (old-style).
1436  //   3) Unsuffixed (even worse).
1437  string16 tested_suffix;
1438  if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) &&
1439      (!GetUserSpecificRegistrySuffix(&tested_suffix) ||
1440       !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
1441                                CONFIRM_PROGID_REGISTRATION)) &&
1442      (!GetOldUserSpecificRegistrySuffix(&tested_suffix) ||
1443       !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
1444                                CONFIRM_PROGID_REGISTRATION)) &&
1445      !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix.erase(),
1446                               CONFIRM_PROGID_REGISTRATION)) {
1447    // If Chrome is not registered under any of the possible suffixes (e.g.
1448    // tests, Canary, etc.): use the new-style suffix at run-time.
1449    if (!GetUserSpecificRegistrySuffix(&tested_suffix))
1450      NOTREACHED();
1451  }
1452  return tested_suffix;
1453}
1454
1455string16 ShellUtil::GetApplicationName(BrowserDistribution* dist,
1456                                       const string16& chrome_exe) {
1457  string16 app_name = dist->GetBaseAppName();
1458  app_name += GetCurrentInstallationSuffix(dist, chrome_exe);
1459  return app_name;
1460}
1461
1462string16 ShellUtil::GetBrowserModelId(BrowserDistribution* dist,
1463                                      bool is_per_user_install) {
1464  string16 app_id(dist->GetBaseAppId());
1465  string16 suffix;
1466
1467  // TODO(robertshield): Temporary hack to make the kRegisterChromeBrowserSuffix
1468  // apply to all registry values computed down in these murky depths.
1469  CommandLine& command_line = *CommandLine::ForCurrentProcess();
1470  if (command_line.HasSwitch(
1471          installer::switches::kRegisterChromeBrowserSuffix)) {
1472    suffix = command_line.GetSwitchValueNative(
1473        installer::switches::kRegisterChromeBrowserSuffix);
1474  } else if (is_per_user_install && !GetUserSpecificRegistrySuffix(&suffix)) {
1475    NOTREACHED();
1476  }
1477  // There is only one component (i.e. the suffixed appid) in this case, but it
1478  // is still necessary to go through the appid constructor to make sure the
1479  // returned appid is truncated if necessary.
1480  std::vector<string16> components(1, app_id.append(suffix));
1481  return BuildAppModelId(components);
1482}
1483
1484string16 ShellUtil::BuildAppModelId(
1485    const std::vector<string16>& components) {
1486  DCHECK_GT(components.size(), 0U);
1487
1488  // Find the maximum numbers of characters allowed in each component
1489  // (accounting for the dots added between each component).
1490  const size_t available_chars =
1491      installer::kMaxAppModelIdLength - (components.size() - 1);
1492  const size_t max_component_length = available_chars / components.size();
1493
1494  // |max_component_length| should be at least 2; otherwise the truncation logic
1495  // below breaks.
1496  if (max_component_length < 2U) {
1497    NOTREACHED();
1498    return (*components.begin()).substr(0, installer::kMaxAppModelIdLength);
1499  }
1500
1501  string16 app_id;
1502  app_id.reserve(installer::kMaxAppModelIdLength);
1503  for (std::vector<string16>::const_iterator it = components.begin();
1504       it != components.end(); ++it) {
1505    if (it != components.begin())
1506      app_id.push_back(L'.');
1507
1508    const string16& component = *it;
1509    DCHECK(!component.empty());
1510    if (component.length() > max_component_length) {
1511      // Append a shortened version of this component. Cut in the middle to try
1512      // to avoid losing the unique parts of this component (which are usually
1513      // at the beginning or end for things like usernames and paths).
1514      app_id.append(component.c_str(), 0, max_component_length / 2);
1515      app_id.append(component.c_str(),
1516                    component.length() - ((max_component_length + 1) / 2),
1517                    string16::npos);
1518    } else {
1519      app_id.append(component);
1520    }
1521  }
1522  // No spaces are allowed in the AppUserModelId according to MSDN.
1523  ReplaceChars(app_id, L" ", L"_", &app_id);
1524  return app_id;
1525}
1526
1527ShellUtil::DefaultState ShellUtil::GetChromeDefaultState() {
1528  // When we check for default browser we don't necessarily want to count file
1529  // type handlers and icons as having changed the default browser status,
1530  // since the user may have changed their shell settings to cause HTML files
1531  // to open with a text editor for example. We also don't want to aggressively
1532  // claim FTP, since the user may have a separate FTP client. It is an open
1533  // question as to how to "heal" these settings. Perhaps the user should just
1534  // re-run the installer or run with the --set-default-browser command line
1535  // flag. There is doubtless some other key we can hook into to cause "Repair"
1536  // to show up in Add/Remove programs for us.
1537  static const wchar_t* const kChromeProtocols[] = { L"http", L"https" };
1538  return ProbeProtocolHandlers(kChromeProtocols, arraysize(kChromeProtocols));
1539}
1540
1541ShellUtil::DefaultState ShellUtil::GetChromeDefaultProtocolClientState(
1542    const string16& protocol) {
1543  if (protocol.empty())
1544    return UNKNOWN_DEFAULT;
1545
1546  const wchar_t* const protocols[] = { protocol.c_str() };
1547  return ProbeProtocolHandlers(protocols, arraysize(protocols));
1548}
1549
1550// static
1551bool ShellUtil::CanMakeChromeDefaultUnattended() {
1552  return base::win::GetVersion() < base::win::VERSION_WIN8;
1553}
1554
1555bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist,
1556                                  int shell_change,
1557                                  const string16& chrome_exe,
1558                                  bool elevate_if_not_admin) {
1559  DCHECK(!(shell_change & ShellUtil::SYSTEM_LEVEL) || IsUserAnAdmin());
1560
1561  if (!dist->CanSetAsDefault())
1562    return false;
1563
1564  // Windows 8 does not permit making a browser default just like that.
1565  // This process needs to be routed through the system's UI. Use
1566  // ShowMakeChromeDefaultSystemUI instead (below).
1567  if (!CanMakeChromeDefaultUnattended()) {
1568    return false;
1569  }
1570
1571  if (!ShellUtil::RegisterChromeBrowser(
1572          dist, chrome_exe, string16(), elevate_if_not_admin)) {
1573    return false;
1574  }
1575
1576  bool ret = true;
1577  // First use the new "recommended" way on Vista to make Chrome default
1578  // browser.
1579  string16 app_name = GetApplicationName(dist, chrome_exe);
1580
1581  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
1582    // On Windows Vista and Win7 we still can set ourselves via the
1583    // the IApplicationAssociationRegistration interface.
1584    VLOG(1) << "Registering Chrome as default browser on Vista.";
1585    base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR;
1586    HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration,
1587        NULL, CLSCTX_INPROC);
1588    if (SUCCEEDED(hr)) {
1589      for (int i = 0; ShellUtil::kBrowserProtocolAssociations[i] != NULL; i++) {
1590        hr = pAAR->SetAppAsDefault(app_name.c_str(),
1591            ShellUtil::kBrowserProtocolAssociations[i], AT_URLPROTOCOL);
1592        if (!SUCCEEDED(hr)) {
1593          ret = false;
1594          LOG(ERROR) << "Failed to register as default for protocol "
1595                     << ShellUtil::kBrowserProtocolAssociations[i]
1596                     << " (" << hr << ")";
1597        }
1598      }
1599
1600      for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
1601        hr = pAAR->SetAppAsDefault(app_name.c_str(),
1602            ShellUtil::kFileAssociations[i], AT_FILEEXTENSION);
1603        if (!SUCCEEDED(hr)) {
1604          ret = false;
1605          LOG(ERROR) << "Failed to register as default for file extension "
1606                     << ShellUtil::kFileAssociations[i] << " (" << hr << ")";
1607        }
1608      }
1609    }
1610  }
1611
1612  if (!RegisterChromeAsDefaultXPStyle(dist, shell_change, chrome_exe))
1613    ret = false;
1614
1615  // Send Windows notification event so that it can update icons for
1616  // file associations.
1617  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
1618  return ret;
1619}
1620
1621bool ShellUtil::ShowMakeChromeDefaultSystemUI(BrowserDistribution* dist,
1622                                              const string16& chrome_exe) {
1623  DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
1624  if (!dist->CanSetAsDefault())
1625    return false;
1626
1627  if (!RegisterChromeBrowser(dist, chrome_exe, string16(), true))
1628      return false;
1629
1630  bool succeeded = true;
1631  bool is_default = (GetChromeDefaultState() == IS_DEFAULT);
1632  if (!is_default) {
1633    // On Windows 8, you can't set yourself as the default handler
1634    // programatically. In other words IApplicationAssociationRegistration
1635    // has been rendered useless. What you can do is to launch
1636    // "Set Program Associations" section of the "Default Programs"
1637    // control panel, which is a mess, or pop the concise "How you want to open
1638    // webpages?" dialog.  We choose the latter.
1639    succeeded = LaunchSelectDefaultProtocolHandlerDialog(L"http");
1640    is_default = (succeeded && GetChromeDefaultState() == IS_DEFAULT);
1641  }
1642  if (succeeded && is_default)
1643    RegisterChromeAsDefaultXPStyle(dist, CURRENT_USER, chrome_exe);
1644  return succeeded;
1645}
1646
1647bool ShellUtil::MakeChromeDefaultProtocolClient(BrowserDistribution* dist,
1648                                                const string16& chrome_exe,
1649                                                const string16& protocol) {
1650  if (!dist->CanSetAsDefault())
1651    return false;
1652
1653  if (!RegisterChromeForProtocol(dist, chrome_exe, string16(), protocol, true))
1654    return false;
1655
1656  // Windows 8 does not permit making a browser default just like that.
1657  // This process needs to be routed through the system's UI. Use
1658  // ShowMakeChromeDefaultProocolClientSystemUI instead (below).
1659  if (!CanMakeChromeDefaultUnattended())
1660    return false;
1661
1662  bool ret = true;
1663  // First use the new "recommended" way on Vista to make Chrome default
1664  // protocol handler.
1665  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
1666    VLOG(1) << "Registering Chrome as default handler for " << protocol
1667            << " on Vista.";
1668    base::win::ScopedComPtr<IApplicationAssociationRegistration> pAAR;
1669    HRESULT hr = pAAR.CreateInstance(CLSID_ApplicationAssociationRegistration,
1670      NULL, CLSCTX_INPROC);
1671    if (SUCCEEDED(hr)) {
1672      string16 app_name = GetApplicationName(dist, chrome_exe);
1673      hr = pAAR->SetAppAsDefault(app_name.c_str(), protocol.c_str(),
1674                                 AT_URLPROTOCOL);
1675    }
1676    if (!SUCCEEDED(hr)) {
1677      ret = false;
1678      LOG(ERROR) << "Could not make Chrome default protocol client (Vista):"
1679                 << " HRESULT=" << hr << ".";
1680    }
1681  }
1682
1683  // Now use the old way to associate Chrome with the desired protocol. This
1684  // should not be required on Vista+, but since some applications still read
1685  // Software\Classes\<protocol> key directly, do this on Vista+ also.
1686  if (!RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol))
1687    ret = false;
1688
1689  return ret;
1690}
1691
1692bool ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
1693    BrowserDistribution* dist,
1694    const string16& chrome_exe,
1695    const string16& protocol) {
1696  DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8);
1697  if (!dist->CanSetAsDefault())
1698    return false;
1699
1700  if (!RegisterChromeForProtocol(dist, chrome_exe, string16(), protocol, true))
1701    return false;
1702
1703  bool succeeded = true;
1704  bool is_default = (
1705      GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT);
1706  if (!is_default) {
1707    // On Windows 8, you can't set yourself as the default handler
1708    // programatically. In other words IApplicationAssociationRegistration
1709    // has been rendered useless. What you can do is to launch
1710    // "Set Program Associations" section of the "Default Programs"
1711    // control panel, which is a mess, or pop the concise "How you want to open
1712    // links of this type (protocol)?" dialog.  We choose the latter.
1713    succeeded = LaunchSelectDefaultProtocolHandlerDialog(protocol.c_str());
1714    is_default = (succeeded &&
1715                  GetChromeDefaultProtocolClientState(protocol) == IS_DEFAULT);
1716  }
1717  if (succeeded && is_default)
1718    RegisterChromeAsDefaultProtocolClientXPStyle(dist, chrome_exe, protocol);
1719  return succeeded;
1720}
1721
1722bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist,
1723                                      const string16& chrome_exe,
1724                                      const string16& unique_suffix,
1725                                      bool elevate_if_not_admin) {
1726  if (!dist->CanSetAsDefault())
1727    return false;
1728
1729  CommandLine& command_line = *CommandLine::ForCurrentProcess();
1730
1731  string16 suffix;
1732  if (!unique_suffix.empty()) {
1733    suffix = unique_suffix;
1734  } else if (command_line.HasSwitch(
1735                 installer::switches::kRegisterChromeBrowserSuffix)) {
1736    suffix = command_line.GetSwitchValueNative(
1737        installer::switches::kRegisterChromeBrowserSuffix);
1738  } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) {
1739    return false;
1740  }
1741
1742  RemoveRunVerbOnWindows8(dist, chrome_exe);
1743
1744  bool user_level = InstallUtil::IsPerUserInstall(chrome_exe.c_str());
1745  HKEY root = DetermineRegistrationRoot(user_level);
1746
1747  // Look only in HKLM for system-level installs (otherwise, if a user-level
1748  // install is also present, it will lead IsChromeRegistered() to think this
1749  // system-level install isn't registered properly as it is shadowed by the
1750  // user-level install's registrations).
1751  uint32 look_for_in = user_level ?
1752      RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM;
1753
1754  // Check if chrome is already registered with this suffix.
1755  if (IsChromeRegistered(dist, chrome_exe, suffix, look_for_in))
1756    return true;
1757
1758  bool result = true;
1759  if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) {
1760    // Do the full registration if we can do it at user-level or if the user is
1761    // an admin.
1762    ScopedVector<RegistryEntry> progid_and_appreg_entries;
1763    ScopedVector<RegistryEntry> shell_entries;
1764    RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix,
1765                                    &progid_and_appreg_entries);
1766    RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix,
1767                                             &progid_and_appreg_entries);
1768    RegistryEntry::GetShellIntegrationEntries(
1769        dist, chrome_exe, suffix, &shell_entries);
1770    result = (AddRegistryEntries(root, progid_and_appreg_entries) &&
1771              AddRegistryEntries(root, shell_entries));
1772  } else if (elevate_if_not_admin &&
1773             base::win::GetVersion() >= base::win::VERSION_VISTA &&
1774             ElevateAndRegisterChrome(dist, chrome_exe, suffix, L"")) {
1775    // If the user is not an admin and OS is between Vista and Windows 7
1776    // inclusively, try to elevate and register. This is only intended for
1777    // user-level installs as system-level installs should always be run with
1778    // admin rights.
1779    result = true;
1780  } else {
1781    // If we got to this point then all we can do is create ProgId and basic app
1782    // registrations under HKCU.
1783    ScopedVector<RegistryEntry> entries;
1784    RegistryEntry::GetProgIdEntries(dist, chrome_exe, string16(), &entries);
1785    // Prefer to use |suffix|; unless Chrome's ProgIds are already registered
1786    // with no suffix (as per the old registration style): in which case some
1787    // other registry entries could refer to them and since we were not able to
1788    // set our HKLM entries above, we are better off not altering these here.
1789    if (!AreEntriesRegistered(entries, RegistryEntry::LOOK_IN_HKCU)) {
1790      if (!suffix.empty()) {
1791        entries.clear();
1792        RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries);
1793        RegistryEntry::GetAppRegistrationEntries(chrome_exe, suffix, &entries);
1794      }
1795      result = AddRegistryEntries(HKEY_CURRENT_USER, entries);
1796    } else {
1797      // The ProgId is registered unsuffixed in HKCU, also register the app with
1798      // Windows in HKCU (this was not done in the old registration style and
1799      // thus needs to be done after the above check for the unsuffixed
1800      // registration).
1801      entries.clear();
1802      RegistryEntry::GetAppRegistrationEntries(chrome_exe, string16(),
1803                                               &entries);
1804      result = AddRegistryEntries(HKEY_CURRENT_USER, entries);
1805    }
1806  }
1807  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
1808  return result;
1809}
1810
1811bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist,
1812                                          const string16& chrome_exe,
1813                                          const string16& unique_suffix,
1814                                          const string16& protocol,
1815                                          bool elevate_if_not_admin) {
1816  if (!dist->CanSetAsDefault())
1817    return false;
1818
1819  string16 suffix;
1820  if (!unique_suffix.empty()) {
1821    suffix = unique_suffix;
1822  } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) {
1823    return false;
1824  }
1825
1826  bool user_level = InstallUtil::IsPerUserInstall(chrome_exe.c_str());
1827  HKEY root = DetermineRegistrationRoot(user_level);
1828
1829  // Look only in HKLM for system-level installs (otherwise, if a user-level
1830  // install is also present, it could lead IsChromeRegisteredForProtocol() to
1831  // think this system-level install isn't registered properly as it may be
1832  // shadowed by the user-level install's registrations).
1833  uint32 look_for_in = user_level ?
1834      RegistryEntry::LOOK_IN_HKCU_THEN_HKLM : RegistryEntry::LOOK_IN_HKLM;
1835
1836  // Check if chrome is already registered with this suffix.
1837  if (IsChromeRegisteredForProtocol(dist, suffix, protocol, look_for_in))
1838    return true;
1839
1840  if (root == HKEY_CURRENT_USER || IsUserAnAdmin()) {
1841    // We can do this operation directly.
1842    // First, make sure Chrome is fully registered on this machine.
1843    if (!RegisterChromeBrowser(dist, chrome_exe, suffix, false))
1844      return false;
1845
1846    // Write in the capabillity for the protocol.
1847    ScopedVector<RegistryEntry> entries;
1848    RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol,
1849                                                &entries);
1850    return AddRegistryEntries(root, entries);
1851  } else if (elevate_if_not_admin &&
1852             base::win::GetVersion() >= base::win::VERSION_VISTA) {
1853    // Elevate to do the whole job
1854    return ElevateAndRegisterChrome(dist, chrome_exe, suffix, protocol);
1855  } else {
1856    // Admin rights are required to register capabilities before Windows 8.
1857    return false;
1858  }
1859}
1860
1861bool ShellUtil::RemoveShortcut(ShellUtil::ShortcutLocation location,
1862                               BrowserDistribution* dist,
1863                               const base::FilePath& target_exe,
1864                               ShellChange level,
1865                               const string16* shortcut_name) {
1866  const bool delete_folder = (location == SHORTCUT_LOCATION_START_MENU);
1867
1868  base::FilePath shortcut_folder;
1869  if (!GetShortcutPath(location, dist, level, &shortcut_folder) ||
1870      shortcut_folder.empty()) {
1871    NOTREACHED();
1872    return false;
1873  }
1874
1875  if (!delete_folder && !shortcut_name) {
1876    file_util::FileEnumerator enumerator(shortcut_folder, false,
1877        file_util::FileEnumerator::FILES);
1878    bool had_failures = false;
1879    for (base::FilePath path = enumerator.Next(); !path.empty();
1880         path = enumerator.Next()) {
1881      if (path.Extension() != installer::kLnkExt)
1882        continue;
1883
1884      if (!MaybeRemoveShortcutAtPath(path, target_exe, delete_folder))
1885        had_failures = true;
1886    }
1887    return !had_failures;
1888  }
1889
1890  const string16 shortcut_base_name(
1891      (shortcut_name ? *shortcut_name : dist->GetAppShortCutName()) +
1892      installer::kLnkExt);
1893  const base::FilePath shortcut_path(
1894      shortcut_folder.Append(shortcut_base_name));
1895  if (!file_util::PathExists(shortcut_path))
1896    return true;
1897
1898  return MaybeRemoveShortcutAtPath(shortcut_path, target_exe, delete_folder);
1899}
1900
1901void ShellUtil::RemoveTaskbarShortcuts(const string16& target_exe) {
1902  if (base::win::GetVersion() < base::win::VERSION_WIN7)
1903    return;
1904
1905  base::FilePath taskbar_pins_path;
1906  if (!PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_pins_path) ||
1907      !file_util::PathExists(taskbar_pins_path)) {
1908    LOG(ERROR) << "Couldn't find path to taskbar pins.";
1909    return;
1910  }
1911
1912  file_util::FileEnumerator shortcuts_enum(
1913      taskbar_pins_path, false,
1914      file_util::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
1915
1916  base::FilePath target_path(target_exe);
1917  InstallUtil::ProgramCompare target_compare(target_path);
1918  for (base::FilePath shortcut_path = shortcuts_enum.Next();
1919       !shortcut_path.empty();
1920       shortcut_path = shortcuts_enum.Next()) {
1921    base::FilePath read_target;
1922    if (!base::win::ResolveShortcut(shortcut_path, &read_target, NULL)) {
1923      LOG(ERROR) << "Couldn't resolve shortcut at " << shortcut_path.value();
1924      continue;
1925    }
1926    if (target_compare.EvaluatePath(read_target)) {
1927      // Unpin this shortcut if it points to |target_exe|.
1928      base::win::TaskbarUnpinShortcutLink(shortcut_path.value().c_str());
1929    }
1930  }
1931}
1932
1933void ShellUtil::RemoveStartScreenShortcuts(BrowserDistribution* dist,
1934                                           const string16& target_exe) {
1935  if (base::win::GetVersion() < base::win::VERSION_WIN8)
1936    return;
1937
1938  base::FilePath app_shortcuts_path;
1939  if (!PathService::Get(base::DIR_APP_SHORTCUTS, &app_shortcuts_path)) {
1940    LOG(ERROR) << "Could not get application shortcuts location to delete"
1941               << " start screen shortcuts.";
1942    return;
1943  }
1944
1945  app_shortcuts_path = app_shortcuts_path.Append(
1946      GetBrowserModelId(dist,
1947                        InstallUtil::IsPerUserInstall(target_exe.c_str())));
1948  if (!file_util::DirectoryExists(app_shortcuts_path)) {
1949    VLOG(1) << "No start screen shortcuts to delete.";
1950    return;
1951  }
1952
1953  VLOG(1) << "Removing start screen shortcuts from "
1954          << app_shortcuts_path.value();
1955  if (!file_util::Delete(app_shortcuts_path, true)) {
1956    LOG(ERROR) << "Failed to remove start screen shortcuts from "
1957               << app_shortcuts_path.value();
1958  }
1959}
1960
1961bool ShellUtil::GetUserSpecificRegistrySuffix(string16* suffix) {
1962  // Use a thread-safe cache for the user's suffix.
1963  static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance =
1964      LAZY_INSTANCE_INITIALIZER;
1965  return suffix_instance.Get().GetSuffix(suffix);
1966}
1967
1968bool ShellUtil::GetOldUserSpecificRegistrySuffix(string16* suffix) {
1969  wchar_t user_name[256];
1970  DWORD size = arraysize(user_name);
1971  if (::GetUserName(user_name, &size) == 0 || size < 1) {
1972    NOTREACHED();
1973    return false;
1974  }
1975  suffix->reserve(size);
1976  suffix->assign(1, L'.');
1977  suffix->append(user_name, size - 1);
1978  return true;
1979}
1980
1981string16 ShellUtil::ByteArrayToBase32(const uint8* bytes, size_t size) {
1982  static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
1983
1984  // Eliminate special cases first.
1985  if (size == 0) {
1986    return string16();
1987  } else if (size == 1) {
1988    string16 ret;
1989    ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]);
1990    ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]);
1991    return ret;
1992  } else if (size >= std::numeric_limits<size_t>::max() / 8) {
1993    // If |size| is too big, the calculation of |encoded_length| below will
1994    // overflow.
1995    NOTREACHED();
1996    return string16();
1997  }
1998
1999  // Overestimate the number of bits in the string by 4 so that dividing by 5
2000  // is the equivalent of rounding up the actual number of bits divided by 5.
2001  const size_t encoded_length = (size * 8 + 4) / 5;
2002
2003  string16 ret;
2004  ret.reserve(encoded_length);
2005
2006  // A bit stream which will be read from the left and appended to from the
2007  // right as it's emptied.
2008  uint16 bit_stream = (bytes[0] << 8) + bytes[1];
2009  size_t next_byte_index = 2;
2010  int free_bits = 0;
2011  while (free_bits < 16) {
2012    // Extract the 5 leftmost bits in the stream
2013    ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]);
2014    bit_stream <<= 5;
2015    free_bits += 5;
2016
2017    // If there is enough room in the bit stream, inject another byte (if there
2018    // are any left...).
2019    if (free_bits >= 8 && next_byte_index < size) {
2020      free_bits -= 8;
2021      bit_stream += bytes[next_byte_index++] << free_bits;
2022    }
2023  }
2024
2025  DCHECK_EQ(ret.length(), encoded_length);
2026  return ret;
2027}
2028