From 4b636fa73856bc6b58abc850e1d1095d6592581d Mon Sep 17 00:00:00 2001
From: "kuchhal@chromium.org"
 <kuchhal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Wed, 8 Oct 2008 17:15:52 +0000
Subject: Fix some problems with machine level install.

BUG=2380

Review URL: http://codereview.chromium.org/6402

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3015 0039d316-1c4b-4281-b951-d872f2087c98
---
 base/file_util.h                                   |  4 ++
 base/file_util_win.cc                              |  7 +++
 chrome/installer/setup/install.cc                  |  4 ++
 chrome/installer/setup/main.cc                     |  2 +-
 chrome/installer/setup/setup.cc                    |  5 ++
 chrome/installer/setup/uninstall.cc                | 66 +++++++++++++++-------
 chrome/installer/util/browser_distribution.cc      |  3 -
 chrome/installer/util/browser_distribution.h       |  2 -
 .../installer/util/google_chrome_distribution.cc   | 19 -------
 chrome/installer/util/google_chrome_distribution.h |  2 -
 10 files changed, 66 insertions(+), 48 deletions(-)

diff --git a/base/file_util.h b/base/file_util.h
index b060579..9624e60 100644
--- a/base/file_util.h
+++ b/base/file_util.h
@@ -208,6 +208,10 @@ bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
                         const wchar_t *working_dir, const wchar_t *arguments,
                         const wchar_t *description, const wchar_t *icon,
                         int icon_index);
+
+// Return true if the given directory is empty
+bool IsDirectoryEmpty(const std::wstring& dir_path);
+
 #endif
 
   
diff --git a/base/file_util_win.cc b/base/file_util_win.cc
index fc53dc9..8253e53 100644
--- a/base/file_util_win.cc
+++ b/base/file_util_win.cc
@@ -365,6 +365,13 @@ bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
   return SUCCEEDED(result);
 }
 
+bool IsDirectoryEmpty(const std::wstring& dir_path) {
+  FileEnumerator files(dir_path, false, FileEnumerator::FILES_AND_DIRECTORIES);
+  if (files.Next().empty())
+    return true;
+  return false;
+}
+
 bool GetTempDir(std::wstring* path) {
   wchar_t temp_path[MAX_PATH + 1];
   DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc
index 4f41e04..5d65dbf 100644
--- a/chrome/installer/setup/install.cc
+++ b/chrome/installer/setup/install.cc
@@ -50,6 +50,10 @@ void AddUninstallShortcutWorkItems(HKEY reg_root,
                           file_util::GetFilenameFromPath(exe_path));
   uninstall_cmd.append(L"\" --");
   uninstall_cmd.append(installer_util::switches::kUninstall);
+  if (reg_root == HKEY_LOCAL_MACHINE) {
+    uninstall_cmd.append(L" --");
+    uninstall_cmd.append(installer_util::switches::kSystemInstall);
+  }
 
   // Create DisplayName, UninstallString and InstallLocation keys
   BrowserDistribution* dist = BrowserDistribution::GetDistribution();
diff --git a/chrome/installer/setup/main.cc b/chrome/installer/setup/main.cc
index 54e66b8..9714f68 100644
--- a/chrome/installer/setup/main.cc
+++ b/chrome/installer/setup/main.cc
@@ -272,7 +272,7 @@ installer_util::InstallStatus InstallChrome(const CommandLine& cmd_line,
 
   BrowserDistribution* dist = BrowserDistribution::GetDistribution();
   dist->UpdateDiffInstallStatus(system_install, incremental_install,
-                                 install_status);
+                                install_status);
   return install_status;
 }
 
diff --git a/chrome/installer/setup/setup.cc b/chrome/installer/setup/setup.cc
index 2ac4dfa..e3edf19 100644
--- a/chrome/installer/setup/setup.cc
+++ b/chrome/installer/setup/setup.cc
@@ -130,6 +130,11 @@ bool CreateOrUpdateChromeShortcuts(const std::wstring& exe_path,
                             file_util::GetFilenameFromPath(exe_path));
     std::wstring arguments(L" --");
     arguments.append(installer_util::switches::kUninstall);
+    if (system_install) {
+      arguments.append(L" --");
+      arguments.append(installer_util::switches::kSystemInstall);
+    }
+
     LOG(INFO) << "Creating/updating uninstall link at " << uninstall_link;
     std::wstring target_folder = file_util::GetDirectoryFromPath(install_path);
     ret2 = file_util::CreateShortcutLink(setup_exe.c_str(),
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
index f4cb697..4988e54 100644
--- a/chrome/installer/setup/uninstall.cc
+++ b/chrome/installer/setup/uninstall.cc
@@ -57,6 +57,49 @@ void DeleteChromeShortcut(bool system_uninstall) {
   }
 }
 
+// Deletes all installed files of Chromium and Folders. Before deleting it
+// needs to move setup.exe in a temp folder because the current process
+// is using that file. It returns false when it can not get the path to
+// installation folder, in all other cases it returns true even in case
+// of error (only logs the error).
+bool DeleteFilesAndFolders(const std::wstring& exe_path, bool system_uninstall,
+    const installer::Version& installed_version) {
+  std::wstring install_path(installer::GetChromeInstallPath(system_uninstall));
+  if (install_path.empty()) {
+    LOG(ERROR) << "Could not get installation destination path.";
+    return false; // Nothing else we can do for uninstall, so we return.
+  } else {
+    LOG(INFO) << "install destination path: " << install_path;
+  }
+
+  std::wstring setup_exe(installer::GetInstallerPathUnderChrome(
+      install_path, installed_version.GetString()));
+  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;
+
+  // Now check and delete if the parent directories are empty
+  // For example Google\Chrome or Chromium
+  std::wstring parent_dir = file_util::GetDirectoryFromPath(install_path);
+  if (!parent_dir.empty() && file_util::IsDirectoryEmpty(parent_dir)) {
+    if (!file_util::Delete(parent_dir, true))
+      LOG(ERROR) << "Failed to delete folder: " << parent_dir;
+    parent_dir = file_util::GetDirectoryFromPath(parent_dir);
+    if (!parent_dir.empty() &&
+        file_util::IsDirectoryEmpty(parent_dir)) {
+      if (!file_util::Delete(parent_dir, true))
+        LOG(ERROR) << "Failed to delete folder: " << parent_dir;
+    }
+  }
+  return true;
+}
+
 // 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.
@@ -126,8 +169,6 @@ installer_util::InstallStatus installer_setup::UninstallChrome(
   if (status != installer_util::UNINSTALL_CONFIRMED)
     return status;
 
-  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
-  dist->DoPreUninstallOperations();
 #if defined(GOOGLE_CHROME_BUILD)
   // TODO(rahulk): This should be done by DoPreUninstallOperations call above
   wchar_t product[39];  // GUID + '\0'
@@ -151,6 +192,7 @@ installer_util::InstallStatus installer_setup::UninstallChrome(
   // Delete the registry keys (Uninstall key and Version key).
   HKEY reg_root = system_uninstall ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
   RegKey key(reg_root, L"", KEY_ALL_ACCESS);
+  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
   DeleteRegistryKey(key, dist->GetUninstallRegPath());
   DeleteRegistryKey(key, dist->GetVersionKey());
 
@@ -188,26 +230,8 @@ installer_util::InstallStatus installer_setup::UninstallChrome(
 
   // 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) << "Could not get installation destination path.";
-    // Nothing else we could do for uninstall, so we return.
+  if (!DeleteFilesAndFolders(exe_path, system_uninstall, installed_version))
     return installer_util::UNINSTALL_FAILED;
-  } else {
-    LOG(INFO) << "install destination path: " << install_path;
-  }
-
-  std::wstring setup_exe(installer::GetInstallerPathUnderChrome(
-      install_path, installed_version.GetString()));
-  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.";
   dist->DoPostUninstallOperations(installed_version);
diff --git a/chrome/installer/util/browser_distribution.cc b/chrome/installer/util/browser_distribution.cc
index c34dca8..914179c 100644
--- a/chrome/installer/util/browser_distribution.cc
+++ b/chrome/installer/util/browser_distribution.cc
@@ -26,9 +26,6 @@ void BrowserDistribution::DoPostUninstallOperations(
     const installer::Version& version) {
 }
 
-void BrowserDistribution::DoPreUninstallOperations() {
-}
-
 std::wstring BrowserDistribution::GetApplicationName() {
   return L"Chromium";
 }
diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h
index e678417f..627ae75 100644
--- a/chrome/installer/util/browser_distribution.h
+++ b/chrome/installer/util/browser_distribution.h
@@ -19,8 +19,6 @@ class BrowserDistribution {
 
   virtual void DoPostUninstallOperations(const installer::Version& version);
 
-  virtual void DoPreUninstallOperations();
-
   virtual std::wstring GetApplicationName();
 
   virtual std::wstring GetInstallSubDir();
diff --git a/chrome/installer/util/google_chrome_distribution.cc b/chrome/installer/util/google_chrome_distribution.cc
index e9c7b01..f5ea433 100644
--- a/chrome/installer/util/google_chrome_distribution.cc
+++ b/chrome/installer/util/google_chrome_distribution.cc
@@ -73,25 +73,6 @@ void GoogleChromeDistribution::DoPostUninstallOperations(
   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 GoogleChromeDistribution::DoPreUninstallOperations() {
-  /* TODO(rahulk) this comment is commented for now because it is causing extra
-  dependencies for the renderer. Need to remove ifdef from uninstall.cc and
-  uncomment this function.
-  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 << ": " << ret;
-  }*/
-}
-
 std::wstring GoogleChromeDistribution::GetApplicationName() {
   const std::wstring& product_name =
       installer_util::GetLocalizedString(IDS_PRODUCT_NAME_BASE);
diff --git a/chrome/installer/util/google_chrome_distribution.h b/chrome/installer/util/google_chrome_distribution.h
index 9b15180..9e803e0 100644
--- a/chrome/installer/util/google_chrome_distribution.h
+++ b/chrome/installer/util/google_chrome_distribution.h
@@ -15,8 +15,6 @@ class GoogleChromeDistribution : public BrowserDistribution {
  public:
   virtual void DoPostUninstallOperations(const installer::Version& version);
 
-  virtual void DoPreUninstallOperations();
-
   virtual std::wstring GetApplicationName();
 
   virtual std::wstring GetInstallSubDir();
-- 
cgit v1.1