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 specific implementation of BrowserDistribution class for 6 // Google Chrome. 7 8 #include "chrome/installer/util/google_chrome_distribution.h" 9 10 #include <windows.h> 11 #include <msi.h> 12 13 #include "base/files/file_path.h" 14 #include "base/path_service.h" 15 #include "base/strings/string_util.h" 16 #include "base/strings/stringprintf.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "base/win/registry.h" 19 #include "base/win/windows_version.h" 20 #include "chrome/common/net/test_server_locations.h" 21 #include "chrome/installer/util/channel_info.h" 22 #include "chrome/installer/util/google_update_constants.h" 23 #include "chrome/installer/util/google_update_settings.h" 24 #include "chrome/installer/util/helper.h" 25 #include "chrome/installer/util/install_util.h" 26 #include "chrome/installer/util/l10n_string_util.h" 27 #include "chrome/installer/util/uninstall_metrics.h" 28 #include "chrome/installer/util/util_constants.h" 29 #include "chrome/installer/util/wmi.h" 30 #include "content/public/common/result_codes.h" 31 32 #include "installer_util_strings.h" // NOLINT 33 34 namespace { 35 36 const wchar_t kChromeGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; 37 const wchar_t kBrowserAppId[] = L"Chrome"; 38 const wchar_t kCommandExecuteImplUuid[] = 39 L"{5C65F4B0-3651-4514-B207-D10CB699B14B}"; 40 41 // Substitute the locale parameter in uninstall URL with whatever 42 // Google Update tells us is the locale. In case we fail to find 43 // the locale, we use US English. 44 string16 LocalizeUrl(const wchar_t* url) { 45 string16 language; 46 if (!GoogleUpdateSettings::GetLanguage(&language)) 47 language = L"en-US"; // Default to US English. 48 return ReplaceStringPlaceholders(url, language.c_str(), NULL); 49 } 50 51 string16 GetUninstallSurveyUrl() { 52 const wchar_t kSurveyUrl[] = L"http://www.google.com/support/chrome/bin/" 53 L"request.py?hl=$1&contact_type=uninstall"; 54 return LocalizeUrl(kSurveyUrl); 55 } 56 57 } // namespace 58 59 GoogleChromeDistribution::GoogleChromeDistribution() 60 : BrowserDistribution(CHROME_BROWSER), 61 product_guid_(kChromeGuid) { 62 } 63 64 void GoogleChromeDistribution::DoPostUninstallOperations( 65 const Version& version, 66 const base::FilePath& local_data_path, 67 const string16& distribution_data) { 68 // Send the Chrome version and OS version as params to the form. 69 // It would be nice to send the locale, too, but I don't see an 70 // easy way to get that in the existing code. It's something we 71 // can add later, if needed. 72 // We depend on installed_version.GetString() not having spaces or other 73 // characters that need escaping: 0.2.13.4. Should that change, we will 74 // need to escape the string before using it in a URL. 75 const string16 kVersionParam = L"crversion"; 76 const string16 kOSParam = L"os"; 77 base::win::OSInfo::VersionNumber version_number = 78 base::win::OSInfo::GetInstance()->version_number(); 79 string16 os_version = base::StringPrintf(L"%d.%d.%d", 80 version_number.major, version_number.minor, version_number.build); 81 82 base::FilePath iexplore; 83 if (!PathService::Get(base::DIR_PROGRAM_FILES, &iexplore)) 84 return; 85 86 iexplore = iexplore.AppendASCII("Internet Explorer"); 87 iexplore = iexplore.AppendASCII("iexplore.exe"); 88 89 string16 command = iexplore.value() + L" " + GetUninstallSurveyUrl() + 90 L"&" + kVersionParam + L"=" + UTF8ToWide(version.GetString()) + L"&" + 91 kOSParam + L"=" + os_version; 92 93 string16 uninstall_metrics; 94 if (installer::ExtractUninstallMetricsFromFile(local_data_path, 95 &uninstall_metrics)) { 96 // The user has opted into anonymous usage data collection, so append 97 // metrics and distribution data. 98 command += uninstall_metrics; 99 if (!distribution_data.empty()) { 100 command += L"&"; 101 command += distribution_data; 102 } 103 } 104 105 int pid = 0; 106 // The reason we use WMI to launch the process is because the uninstall 107 // process runs inside a Job object controlled by the shell. As long as there 108 // are processes running, the shell will not close the uninstall applet. WMI 109 // allows us to escape from the Job object so the applet will close. 110 installer::WMIProcess::Launch(command, &pid); 111 } 112 113 string16 GoogleChromeDistribution::GetActiveSetupGuid() { 114 return product_guid(); 115 } 116 117 string16 GoogleChromeDistribution::GetAppGuid() { 118 return product_guid(); 119 } 120 121 string16 GoogleChromeDistribution::GetBaseAppName() { 122 // I'd really like to return L ## PRODUCT_FULLNAME_STRING; but that's no good 123 // since it'd be "Chromium" in a non-Chrome build, which isn't at all what I 124 // want. Sigh. 125 return L"Google Chrome"; 126 } 127 128 string16 GoogleChromeDistribution::GetAppShortCutName() { 129 const string16& app_shortcut_name = 130 installer::GetLocalizedString(IDS_PRODUCT_NAME_BASE); 131 return app_shortcut_name; 132 } 133 134 string16 GoogleChromeDistribution::GetAlternateApplicationName() { 135 const string16& alt_product_name = 136 installer::GetLocalizedString(IDS_OEM_MAIN_SHORTCUT_NAME_BASE); 137 return alt_product_name; 138 } 139 140 string16 GoogleChromeDistribution::GetBaseAppId() { 141 return kBrowserAppId; 142 } 143 144 string16 GoogleChromeDistribution::GetInstallSubDir() { 145 string16 sub_dir(installer::kGoogleChromeInstallSubDir1); 146 sub_dir.append(L"\\"); 147 sub_dir.append(installer::kGoogleChromeInstallSubDir2); 148 return sub_dir; 149 } 150 151 string16 GoogleChromeDistribution::GetPublisherName() { 152 const string16& publisher_name = 153 installer::GetLocalizedString(IDS_ABOUT_VERSION_COMPANY_NAME_BASE); 154 return publisher_name; 155 } 156 157 string16 GoogleChromeDistribution::GetAppDescription() { 158 const string16& app_description = 159 installer::GetLocalizedString(IDS_SHORTCUT_TOOLTIP_BASE); 160 return app_description; 161 } 162 163 std::string GoogleChromeDistribution::GetSafeBrowsingName() { 164 return "googlechrome"; 165 } 166 167 string16 GoogleChromeDistribution::GetStateKey() { 168 string16 key(google_update::kRegPathClientState); 169 key.append(L"\\"); 170 key.append(product_guid()); 171 return key; 172 } 173 174 string16 GoogleChromeDistribution::GetStateMediumKey() { 175 string16 key(google_update::kRegPathClientStateMedium); 176 key.append(L"\\"); 177 key.append(product_guid()); 178 return key; 179 } 180 181 std::string GoogleChromeDistribution::GetNetworkStatsServer() const { 182 return chrome_common_net::kEchoTestServerLocation; 183 } 184 185 std::string GoogleChromeDistribution::GetHttpPipeliningTestServer() const { 186 return chrome_common_net::kPipelineTestServerBaseUrl; 187 } 188 189 string16 GoogleChromeDistribution::GetDistributionData(HKEY root_key) { 190 string16 sub_key(google_update::kRegPathClientState); 191 sub_key.append(L"\\"); 192 sub_key.append(product_guid()); 193 194 base::win::RegKey client_state_key(root_key, sub_key.c_str(), KEY_READ); 195 string16 result; 196 string16 brand_value; 197 if (client_state_key.ReadValue(google_update::kRegRLZBrandField, 198 &brand_value) == ERROR_SUCCESS) { 199 result = google_update::kRegRLZBrandField; 200 result.append(L"="); 201 result.append(brand_value); 202 result.append(L"&"); 203 } 204 205 string16 client_value; 206 if (client_state_key.ReadValue(google_update::kRegClientField, 207 &client_value) == ERROR_SUCCESS) { 208 result.append(google_update::kRegClientField); 209 result.append(L"="); 210 result.append(client_value); 211 result.append(L"&"); 212 } 213 214 string16 ap_value; 215 // If we fail to read the ap key, send up "&ap=" anyway to indicate 216 // that this was probably a stable channel release. 217 client_state_key.ReadValue(google_update::kRegApField, &ap_value); 218 result.append(google_update::kRegApField); 219 result.append(L"="); 220 result.append(ap_value); 221 222 return result; 223 } 224 225 string16 GoogleChromeDistribution::GetUninstallLinkName() { 226 const string16& link_name = 227 installer::GetLocalizedString(IDS_UNINSTALL_CHROME_BASE); 228 return link_name; 229 } 230 231 string16 GoogleChromeDistribution::GetUninstallRegPath() { 232 return L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" 233 L"Google Chrome"; 234 } 235 236 string16 GoogleChromeDistribution::GetVersionKey() { 237 string16 key(google_update::kRegPathClients); 238 key.append(L"\\"); 239 key.append(product_guid()); 240 return key; 241 } 242 243 string16 GoogleChromeDistribution::GetIconFilename() { 244 return installer::kChromeExe; 245 } 246 247 bool GoogleChromeDistribution::GetCommandExecuteImplClsid( 248 string16* handler_class_uuid) { 249 if (handler_class_uuid) 250 *handler_class_uuid = kCommandExecuteImplUuid; 251 return true; 252 } 253 254 bool GoogleChromeDistribution::AppHostIsSupported() { 255 return true; 256 } 257 258 // This method checks if we need to change "ap" key in Google Update to try 259 // full installer as fall back method in case incremental installer fails. 260 // - If incremental installer fails we append a magic string ("-full"), if 261 // it is not present already, so that Google Update server next time will send 262 // full installer to update Chrome on the local machine 263 // - If we are currently running full installer, we remove this magic 264 // string (if it is present) regardless of whether installer failed or not. 265 // There is no fall-back for full installer :) 266 void GoogleChromeDistribution::UpdateInstallStatus(bool system_install, 267 installer::ArchiveType archive_type, 268 installer::InstallStatus install_status) { 269 GoogleUpdateSettings::UpdateInstallStatus(system_install, 270 archive_type, InstallUtil::GetInstallReturnCode(install_status), 271 product_guid()); 272 } 273 274 bool GoogleChromeDistribution::ShouldSetExperimentLabels() { 275 return true; 276 } 277 278 bool GoogleChromeDistribution::HasUserExperiments() { 279 return true; 280 } 281