summaryrefslogtreecommitdiffstats
path: root/chrome/installer/setup/uninstall.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/installer/setup/uninstall.cc')
-rw-r--r--chrome/installer/setup/uninstall.cc303
1 files changed, 303 insertions, 0 deletions
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
new file mode 100644
index 0000000..9403a8a
--- /dev/null
+++ b/chrome/installer/setup/uninstall.cc
@@ -0,0 +1,303 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file defines the methods useful for uninstalling Chrome.
+
+#include "chrome/installer/setup/uninstall.h"
+
+#include <atlbase.h>
+#include <windows.h>
+#include <msi.h>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/registry.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "base/wmi_util.h"
+#include "chrome/app/google_update_settings.h"
+#include "chrome/app/result_codes.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/installer/setup/setup_constants.h"
+#include "chrome/installer/util/helper.h"
+#include "chrome/installer/util/install_util.h"
+#include "chrome/installer/util/l10n_string_util.h"
+#include "chrome/installer/util/logging_installer.h"
+#include "chrome/installer/util/google_update_constants.h"
+#include "chrome/installer/util/shell_util.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/installer/util/version.h"
+
+#include "setup_resource.h"
+#include "setup_strings.h"
+
+namespace {
+
+// This method deletes Chrome shortcut folder from Windows Start menu. It
+// checks system_uninstall to see if the shortcut is in all users start menu
+// or current user start menu.
+void DeleteChromeShortcut(bool system_uninstall) {
+ std::wstring shortcut_path;
+ if (system_uninstall) {
+ PathService::Get(base::DIR_COMMON_START_MENU, &shortcut_path);
+ } else {
+ PathService::Get(base::DIR_START_MENU, &shortcut_path);
+ }
+ if (shortcut_path.empty()) {
+ LOG(ERROR) << "failed to get location for shortcut";
+ } else {
+ file_util::AppendToPath(&shortcut_path,
+ installer_util::GetLocalizedString(IDS_PRODUCT_NAME_BASE));
+ LOG(INFO) << "Deleting shortcut " << shortcut_path;
+ if (!file_util::Delete(shortcut_path, true))
+ LOG(ERROR) << "Failed to delete folder: " << shortcut_path;
+ }
+}
+
+// This method tries to delete a registry key and logs an error message
+// in case of failure. It returns true if deletion is successful,
+// otherwise false.
+bool DeleteRegistryKey(RegKey& key, const std::wstring& key_path) {
+ LOG(INFO) << "Deleting registry key " << key_path;
+ if (!key.DeleteKey(key_path.c_str())) {
+ LOG(ERROR) << "Failed to delete registry key: " << key_path
+ << " and the error is " << InstallUtil::FormatLastWin32Error();
+ return false;
+ }
+ return true;
+}
+
+// This method tries to delete a registry value and logs an error message
+// in case of failure. It returns true if deletion is successful,
+// otherwise false.
+bool DeleteRegistryValue(HKEY reg_root, const std::wstring& key_path,
+ const std::wstring& value_name) {
+ RegKey key(reg_root, key_path.c_str(), KEY_ALL_ACCESS);
+ LOG(INFO) << "Deleting registry value " << value_name;
+ if (!key.DeleteValue(value_name.c_str())) {
+ LOG(ERROR) << "Failed to delete registry value: " << value_name
+ << " and the error is " << InstallUtil::FormatLastWin32Error();
+ return false;
+ }
+ return true;
+}
+
+// This method checks if Chrome is currently running or if the user has
+// cancelled the uninstall operation by clicking Cancel on the confirmation
+// box that Chrome pops up.
+installer_util::InstallStatus IsChromeActiveOrUserCancelled(
+ bool system_uninstall) {
+ static const std::wstring kCmdLineOptions(L" --uninstall");
+ static const int32 kTimeOutMs = 30000;
+ int32 exit_code = ResultCodes::NORMAL_EXIT;
+ bool is_timeout = false;
+
+ // We ignore all other errors such as whether launching chrome fails,
+ // whether chrome returns UNINSTALL_ERROR, etc.
+ LOG(INFO) << "Launching Chrome to do uninstall tasks.";
+ if (installer::LaunchChromeAndWaitForResult(system_uninstall,
+ kCmdLineOptions,
+ kTimeOutMs,
+ &exit_code,
+ &is_timeout)) {
+ if (is_timeout || exit_code == ResultCodes::UNINSTALL_CHROME_ALIVE) {
+ LOG(ERROR) << "Can't uninstall when chrome is still running";
+ return installer_util::CHROME_RUNNING;
+ } else if (exit_code == ResultCodes::UNINSTALL_USER_CANCEL) {
+ LOG(INFO) << "User cancelled uninstall operation";
+ return installer_util::UNINSTALL_CANCELLED;
+ } else if (exit_code == ResultCodes::UNINSTALL_ERROR) {
+ LOG(ERROR) << "chrome.exe reported error while uninstalling.";
+ return installer_util::UNINSTALL_FAILED;
+ }
+ }
+
+ return installer_util::UNINSTALL_CONFIRMED;
+}
+
+// Read the URL from the resource file and substitute the locale parameter
+// with whatever Google Update tells us is the locale. In case we fail to find
+// the locale, we use US English.
+std::wstring GetUninstallSurveyUrl() {
+ const ATLSTRINGRESOURCEIMAGE* image = AtlGetStringResourceImage(
+ _AtlBaseModule.GetModuleInstance(), IDS_UNINSTALL_SURVEY_URL);
+ DCHECK(image);
+ std::wstring url = std::wstring(image->achString, image->nLength);
+ DCHECK(!url.empty());
+
+ std::wstring language;
+ if (!GoogleUpdateSettings::GetLanguage(&language))
+ language = L"en-US"; // Default to US English.
+
+ return ReplaceStringPlaceholders(url.c_str(), language.c_str(), NULL);
+}
+
+// This method launches an uninstall survey and is called at the end of
+// uninstall process. We are not doing any error checking here as it is
+// not critical to have this survey. If we fail to launch it, we just
+// ignore it silently.
+void LaunchUninstallSurvey(const installer::Version& installed_version) {
+ // Send the Chrome version and OS version as params to the form.
+ // It would be nice to send the locale, too, but I don't see an
+ // easy way to get that in the existing code. It's something we
+ // can add later, if needed.
+ // We depend on installed_version.GetString() not having spaces or other
+ // characters that need escaping: 0.2.13.4. Should that change, we will
+ // need to escape the string before using it in a URL.
+ const std::wstring kVersionParam = L"crversion";
+ const std::wstring kVersion = installed_version.GetString();
+ const std::wstring kOSParam = L"os";
+ std::wstring os_version = L"na";
+ OSVERSIONINFO version_info;
+ version_info.dwOSVersionInfoSize = sizeof version_info;
+ if (GetVersionEx(&version_info)) {
+ os_version = StringPrintf(L"%d.%d.%d",
+ version_info.dwMajorVersion,
+ version_info.dwMinorVersion,
+ version_info.dwBuildNumber);
+ }
+
+ std::wstring iexplore;
+ if (!PathService::Get(base::DIR_PROGRAM_FILES, &iexplore))
+ return;
+
+ file_util::AppendToPath(&iexplore, L"Internet Explorer");
+ file_util::AppendToPath(&iexplore, L"iexplore.exe");
+
+ std::wstring command = iexplore + L" " + GetUninstallSurveyUrl() + L"&" +
+ kVersionParam + L"=" + kVersion + L"&" + kOSParam + L"=" + os_version;
+ int pid = 0;
+ WMIProcessUtil::Launch(command, &pid);
+}
+
+// Uninstall Chrome specific Gears. First we find Gears MSI ProductId (that
+// changes with every new version of Gears) using Gears MSI UpgradeCode (that
+// does not change) and then uninstall Gears using API.
+void UninstallGears() {
+ wchar_t product[39]; // GUID + '\0'
+ MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); // Don't show any UI to user.
+ for (int i = 0; MsiEnumRelatedProducts(google_update::kGearsUpgradeCode, 0, i,
+ product) != ERROR_NO_MORE_ITEMS; ++i) {
+ LOG(INFO) << "Uninstalling Gears - " << product;
+ unsigned int ret = MsiConfigureProduct(product, INSTALLLEVEL_MAXIMUM,
+ INSTALLSTATE_ABSENT);
+ if (ret != ERROR_SUCCESS)
+ LOG(ERROR) << "Failed to uninstall Gears " << product
+ << " because of error " << ret;
+ }
+}
+
+} // namespace
+
+
+installer_util::InstallStatus installer_setup::UninstallChrome(
+ const std::wstring& exe_path, bool system_uninstall,
+ const installer::Version& installed_version, bool remove_all) {
+ installer_util::InstallStatus status =
+ IsChromeActiveOrUserCancelled(system_uninstall);
+ if (status == installer_util::CHROME_RUNNING ||
+ status == installer_util::UNINSTALL_CANCELLED)
+ return status;
+
+ // Uninstall Gears first.
+ UninstallGears();
+
+ // Chrome is not in use so lets uninstall Chrome by deleting various files
+ // and registry entries. Here we will just make best effort and keep going
+ // in case of errors.
+ // First delete shortcut from Start->Programs.
+ DeleteChromeShortcut(system_uninstall);
+
+ // Delete the registry keys (Uninstall key and Google Update update key).
+ HKEY reg_root = system_uninstall ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+ RegKey key(reg_root, L"", KEY_ALL_ACCESS);
+ DeleteRegistryKey(key, installer_util::kUninstallRegPath);
+ DeleteRegistryKey(key, InstallUtil::GetChromeGoogleUpdateKey());
+
+ // Delete Software\Classes\ChromeHTML,
+ // Software\Clients\StartMenuInternet\chrome.exe and
+ // Software\RegisteredApplications\Chrome
+ std::wstring html_prog_id(ShellUtil::kRegClasses);
+ file_util::AppendToPath(&html_prog_id, ShellUtil::kChromeHTMLProgId);
+ DeleteRegistryKey(key, html_prog_id);
+
+ std::wstring set_access_key(ShellUtil::kRegStartMenuInternet);
+ file_util::AppendToPath(&set_access_key, installer_util::kChromeExe);
+ DeleteRegistryKey(key, set_access_key);
+
+ DeleteRegistryValue(reg_root, ShellUtil::kRegRegisteredApplications,
+ installer_util::kApplicationName);
+ key.Close();
+
+ // Delete shared registry keys as well (these require admin rights) if
+ // remove_all option is specified.
+ if (remove_all) {
+ RegKey hklm_key(HKEY_LOCAL_MACHINE, L"", KEY_ALL_ACCESS);
+ DeleteRegistryKey(hklm_key, set_access_key);
+ DeleteRegistryKey(hklm_key, html_prog_id);
+ DeleteRegistryValue(HKEY_LOCAL_MACHINE,
+ ShellUtil::kRegRegisteredApplications,
+ installer_util::kApplicationName);
+
+ // Delete media player registry key that exists only in HKLM.
+ std::wstring reg_path(installer::kMediaPlayerRegPath);
+ file_util::AppendToPath(&reg_path, installer_util::kChromeExe);
+ DeleteRegistryKey(hklm_key, reg_path);
+ hklm_key.Close();
+ }
+
+ // Finally delete all the files from Chrome folder after moving setup.exe
+ // to a temp location.
+ std::wstring install_path(installer::GetChromeInstallPath(system_uninstall));
+ if (install_path.empty()) {
+ LOG(ERROR) << "Couldn't get installation destination path";
+ // Nothing else we could do for uninstall, so we return.
+ return installer_util::UNINSTALL_FAILED;
+ } else {
+ LOG(INFO) << "install destination path: " << install_path;
+ }
+
+ std::wstring setup_exe(install_path);
+ file_util::AppendToPath(&setup_exe, installed_version.GetString());
+ file_util::AppendToPath(&setup_exe, installer::kInstallerDir);
+ file_util::AppendToPath(&setup_exe, file_util::GetFilenameFromPath(exe_path));
+
+ std::wstring temp_file;
+ file_util::CreateTemporaryFileName(&temp_file);
+ file_util::Move(setup_exe, temp_file);
+
+ LOG(INFO) << "Deleting install path " << install_path;
+ if (!file_util::Delete(install_path, true))
+ LOG(ERROR) << "Failed to delete folder: " << install_path;
+
+ LOG(INFO) << "Uninstallation complete. Launching Uninstall survey.";
+ LaunchUninstallSurvey(installed_version);
+ return installer_util::UNINSTALL_SUCCESSFUL;
+}