Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3358)

Unified Diff: chrome/installer/util/shell_util.cc

Issue 10703155: Merge 145596 - Use a better registration suffix that will always be unique while respecting the MSD… (Closed) Base URL: svn://svn.chromium.org/chrome/branches/1180/src/
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/installer/util/shell_util.h ('k') | chrome/installer/util/shell_util_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/installer/util/shell_util.cc
===================================================================
--- chrome/installer/util/shell_util.cc (revision 146242)
+++ chrome/installer/util/shell_util.cc (working copy)
@@ -9,15 +9,18 @@
#include "chrome/installer/util/shell_util.h"
+#include <shlobj.h>
#include <windows.h>
-#include <shlobj.h>
+#include <limits>
#include <list>
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/md5.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/stl_util.h"
@@ -28,6 +31,7 @@
#include "base/values.h"
#include "base/win/registry.h"
#include "base/win/scoped_comptr.h"
+#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
@@ -85,6 +89,80 @@
return VerifyVersionInfo(&min_version_info, type_mask, condition_mask) != 0;
}
+// Returns the current (or installed) browser's ProgId (e.g.
+// "ChromeHTML|suffix|").
+// |suffix| can be the empty string.
+string16 GetBrowserProgId(const string16& suffix) {
+ string16 chrome_html(ShellUtil::kChromeHTMLProgId);
+ chrome_html.append(suffix);
+
+ // ProgIds cannot be longer than 39 characters.
+ // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx.
+ // Make all new registrations comply with this requirement (existing
+ // registrations must be preserved).
+ string16 new_style_suffix;
+ if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) &&
+ suffix == new_style_suffix && chrome_html.length() > 39) {
+ NOTREACHED();
+ chrome_html.erase(39);
+ }
+ return chrome_html;
+}
+
+// This class is used to initialize and cache a base 32 encoding of the md5 hash
+// of this user's sid preceded by a dot.
+// This is guaranteed to be unique on the machine and 27 characters long
+// (including the '.').
+// This is then meant to be used as a suffix on all registrations that may
+// conflict with another user-level Chrome install.
+class UserSpecificRegistrySuffix {
+ public:
+ // All the initialization is done in the constructor to be able to build the
+ // suffix in a thread-safe manner when used in conjunction with a
+ // LazyInstance.
+ UserSpecificRegistrySuffix();
+
+ // Sets |suffix| to the pre-computed suffix cached in this object.
+ // Returns true unless the initialization originally failed.
+ bool GetSuffix(string16* suffix);
+
+ private:
+ string16 suffix_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix);
+}; // class UserSpecificRegistrySuffix
+
+UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() {
+ string16 user_sid;
+ if (!base::win::GetUserSidString(&user_sid)) {
+ NOTREACHED();
+ return;
+ }
+ COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_);
+ base::MD5Digest md5_digest;
+ base::MD5Sum(user_sid.c_str(), user_sid.length(), &md5_digest);
+ const string16 base32_md5(
+ ShellUtil::ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a)));
+ // The value returned by the base32 algorithm above must never change and
+ // must always be 26 characters long (i.e. if someone ever moves this to
+ // base and implements the full base32 algorithm (i.e. with appended '='
+ // signs in the output), they must provide a flag to allow this method to
+ // still request the output with no appended '=' signs).
+ DCHECK_EQ(base32_md5.length(), 26U);
+ suffix_.reserve(base32_md5.length() + 1);
+ suffix_.assign(1, L'.');
+ suffix_.append(base32_md5);
+}
+
+bool UserSpecificRegistrySuffix::GetSuffix(string16* suffix) {
+ if (suffix_.empty()) {
+ NOTREACHED();
+ return false;
+ }
+ suffix->assign(suffix_);
+ return true;
+}
+
// This class represents a single registry entry. The objective is to
// encapsulate all the registry entries required for registering Chrome at one
// place. This class can not be instantiated outside the class and the objects
@@ -181,8 +259,7 @@
// File association ProgId
string16 chrome_html_prog_id(ShellUtil::kRegClasses);
chrome_html_prog_id.push_back(FilePath::kSeparators[0]);
- chrome_html_prog_id.append(ShellUtil::kChromeHTMLProgId);
- chrome_html_prog_id.append(suffix);
+ chrome_html_prog_id.append(GetBrowserProgId(suffix));
entries->push_front(new RegistryEntry(
chrome_html_prog_id, ShellUtil::kChromeHTMLProgIdDesc));
entries->push_front(new RegistryEntry(
@@ -234,7 +311,7 @@
std::list<RegistryEntry*>* entries) {
entries->push_front(new RegistryEntry(
GetCapabilitiesKey(dist, suffix).append(L"\\URLAssociations"),
- protocol, string16(ShellUtil::kChromeHTMLProgId).append(suffix)));
+ protocol, GetBrowserProgId(suffix)));
return true;
}
@@ -295,8 +372,7 @@
entries->push_front(new RegistryEntry(capabilities + L"\\Startmenu",
L"StartMenuInternet", reg_app_name));
- string16 html_prog_id(ShellUtil::kChromeHTMLProgId);
- html_prog_id.append(suffix);
+ string16 html_prog_id(GetBrowserProgId(suffix));
for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
entries->push_front(new RegistryEntry(
capabilities + L"\\FileAssociations",
@@ -365,8 +441,7 @@
const string16& suffix,
std::list<RegistryEntry*>* entries) {
// File extension associations.
- string16 html_prog_id(ShellUtil::kChromeHTMLProgId);
- html_prog_id.append(suffix);
+ string16 html_prog_id(GetBrowserProgId(suffix));
for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
string16 ext_key(ShellUtil::kRegClasses);
ext_key.push_back(FilePath::kSeparators[0]);
@@ -677,8 +752,7 @@
// <root hkey>\Software\Classes\ChromiumHTML[.user]\shell\open\command
key = ShellUtil::kRegClasses;
key.push_back(FilePath::kSeparators[0]);
- key.append(ShellUtil::kChromeHTMLProgId);
- key.append(installation_suffix);
+ key.append(GetBrowserProgId(installation_suffix));
key.append(ShellUtil::kRegShellOpen);
InstallUtil::DeleteRegistryValue(root_key, key,
ShellUtil::kRegDelegateExecute);
@@ -747,28 +821,13 @@
return false;
}
-// Sets |suffix| to this user's username preceded by a dot. This suffix is then
-// meant to be added to all registration that may conflict with another
-// user-level Chrome install.
-// Returns true unless the OS call to retrieve the username fails.
-bool GetUserSpecificRegistrySuffix(string16* suffix) {
- wchar_t user_name[256];
- DWORD size = arraysize(user_name);
- if (::GetUserName(user_name, &size) == 0 || size < 1) {
- PLOG(DFATAL) << "GetUserName failed";
- return false;
- }
- suffix->reserve(size);
- suffix->assign(1, L'.');
- suffix->append(user_name, size - 1);
- return true;
-}
-
-// Sets |suffix| to the current user's username, preceded by a dot, on
-// user-level installs.
-// To support old-style user-level installs however, |suffix| is cleared if
-// the user currently owns the non-suffixed HKLM registrations.
-// |suffix| is also cleared on system-level installs.
+// Sets |suffix| to a 27 character string that is specific to this user on this
+// machine (on user-level installs only).
+// To support old-style user-level installs however, |suffix| is cleared if the
+// user currently owns the non-suffixed HKLM registrations.
+// |suffix| can also be set to the user's username if the current install is
+// suffixed as per the old-style registrations.
+// |suffix| is cleared on system-level installs.
// |suffix| should then be appended to all Chrome properties that may conflict
// with other Chrome user-level installs.
// Returns true unless one of the underlying calls fails.
@@ -782,9 +841,20 @@
// registered with no suffix.
suffix->clear();
return true;
- } else {
- return GetUserSpecificRegistrySuffix(suffix);
}
+
+ // Get the old suffix for the check below.
+ if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix)) {
+ NOTREACHED();
+ return false;
+ }
+ if (QuickIsChromeRegistered(dist, chrome_exe, *suffix,
+ CONFIRM_SHELL_REGISTRATION)) {
+ // Username suffix for installs that are suffixed as per the old-style.
+ return true;
+ }
+
+ return ShellUtil::GetUserSpecificRegistrySuffix(suffix);
}
// Returns the root registry key (HKLM or HKCU) into which shell integration
@@ -1060,12 +1130,29 @@
string16 ShellUtil::GetCurrentInstallationSuffix(BrowserDistribution* dist,
const string16& chrome_exe) {
+ // This method is somewhat the opposite of GetInstallationSpecificSuffix().
+ // In this case we are not trying to determine the current suffix for the
+ // upcoming installation (i.e. not trying to stick to a currently bad
+ // registration style if one is present).
+ // Here we want to determine which suffix we should use at run-time.
+ // In order of preference, we prefer (for user-level installs):
+ // 1) Base 32 encoding of the md5 hash of the user's sid (new-style).
+ // 2) Username (old-style).
+ // 3) Unsuffixed (even worse).
string16 tested_suffix;
- if (!InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ||
- !GetUserSpecificRegistrySuffix(&tested_suffix) ||
- !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
+ if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) &&
+ (!GetUserSpecificRegistrySuffix(&tested_suffix) ||
+ !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
+ CONFIRM_PROGID_REGISTRATION)) &&
+ (!GetOldUserSpecificRegistrySuffix(&tested_suffix) ||
+ !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
+ CONFIRM_PROGID_REGISTRATION)) &&
+ !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix.erase(),
CONFIRM_PROGID_REGISTRATION)) {
- return string16();
+ // If Chrome is not registered under any of the possible suffixes (e.g.
+ // tests, Canary, etc.): use the new-style suffix at run-time.
+ if (!GetUserSpecificRegistrySuffix(&tested_suffix))
+ NOTREACHED();
}
return tested_suffix;
}
@@ -1520,3 +1607,71 @@
app_id.c_str(),
ConvertShellUtilShortcutOptionsToFileUtil(options));
}
+
+bool ShellUtil::GetUserSpecificRegistrySuffix(string16* suffix) {
+ // Use a thread-safe cache for the user's suffix.
+ static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance =
+ LAZY_INSTANCE_INITIALIZER;
+ return suffix_instance.Get().GetSuffix(suffix);
+}
+
+bool ShellUtil::GetOldUserSpecificRegistrySuffix(string16* suffix) {
+ wchar_t user_name[256];
+ DWORD size = arraysize(user_name);
+ if (::GetUserName(user_name, &size) == 0 || size < 1) {
+ NOTREACHED();
+ return false;
+ }
+ suffix->reserve(size);
+ suffix->assign(1, L'.');
+ suffix->append(user_name, size - 1);
+ return true;
+}
+
+string16 ShellUtil::ByteArrayToBase32(const uint8* bytes, size_t size) {
+ static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+ // Eliminate special cases first.
+ if (size == 0) {
+ return string16();
+ } else if (size == 1) {
+ string16 ret;
+ ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]);
+ ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]);
+ return ret;
+ } else if (size >= std::numeric_limits<size_t>::max() / 8) {
+ // If |size| is too big, the calculation of |encoded_length| below will
+ // overflow.
+ NOTREACHED();
+ return string16();
+ }
+
+ // Overestimate the number of bits in the string by 4 so that dividing by 5
+ // is the equivalent of rounding up the actual number of bits divided by 5.
+ const size_t encoded_length = (size * 8 + 4) / 5;
+
+ string16 ret;
+ ret.reserve(encoded_length);
+
+ // A bit stream which will be read from the left and appended to from the
+ // right as it's emptied.
+ uint16 bit_stream = (bytes[0] << 8) + bytes[1];
+ size_t next_byte_index = 2;
+ int free_bits = 0;
+ while (free_bits < 16) {
+ // Extract the 5 leftmost bits in the stream
+ ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]);
+ bit_stream <<= 5;
+ free_bits += 5;
+
+ // If there is enough room in the bit stream, inject another byte (if there
+ // are any left...).
+ if (free_bits >= 8 && next_byte_index < size) {
+ free_bits -= 8;
+ bit_stream += bytes[next_byte_index++] << free_bits;
+ }
+ }
+
+ DCHECK_EQ(ret.length(), encoded_length);
+ return ret;
+}
« no previous file with comments | « chrome/installer/util/shell_util.h ('k') | chrome/installer/util/shell_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698