blob: aaee71bb00c3b17c2b5654fd1a9f5c6b05e2ef8c [file] [log] [blame]
// Copyright 2007-2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
#include "omaha/setup/setup_google_update.h"
#include <msi.h>
#include <atlpath.h>
#include <vector>
#include "base/basictypes.h"
#include "omaha/base/app_util.h"
#include "omaha/base/atlregmapex.h"
#include "omaha/base/const_object_names.h"
#include "omaha/base/const_utils.h"
#include "omaha/base/constants.h"
#include "omaha/base/debug.h"
#include "omaha/base/error.h"
#include "omaha/base/file.h"
#include "omaha/base/highres_timer-win32.h"
#include "omaha/base/logging.h"
#include "omaha/base/omaha_version.h"
#include "omaha/base/path.h"
#include "omaha/base/reg_key.h"
#include "omaha/base/scoped_any.h"
#include "omaha/base/service_utils.h"
#include "omaha/base/utils.h"
#include "omaha/base/user_info.h"
#include "omaha/common/command_line_builder.h"
#include "omaha/common/config_manager.h"
#include "omaha/common/const_cmd_line.h"
#include "omaha/common/const_goopdate.h"
#include "omaha/common/goopdate_utils.h"
#include "omaha/common/scheduled_task_utils.h"
#include "omaha/setup/setup_metrics.h"
#include "omaha/setup/setup_service.h"
namespace omaha {
namespace {
#ifdef _DEBUG
HRESULT VerifyCOMLocalServerRegistration(bool is_machine) {
// TODO(omaha3): Implement this for Omaha 3. Specifically, this code assumes
// Setup is running from the installed location, which is no longer true.
#if 0
// Validate the following:
// * LocalServer32 under CLSID_OnDemandMachineAppsClass or
// CLSID_OnDemandUserAppsClass should be ...Google\Update\GoogleUpdate.exe.
// * InProcServer32 under CLSID of IID_IGoogleUpdate should be
// ...Google\Update\{version}\goopdate.dll.
// * ProxyStubClsid32 under IGoogleUpdate interface should be the CLSID of the
// proxy, which is IID_IGoogleUpdate.
CString base_clsid_key(goopdate_utils::GetHKRoot());
base_clsid_key += _T("\\Software\\Classes\\CLSID\\");
CString ondemand_clsid_key(base_clsid_key);
ondemand_clsid_key += GuidToString(is_machine ?
__uuidof(OnDemandMachineAppsClass) :
__uuidof(OnDemandUserAppsClass));
CString local_server_key(ondemand_clsid_key + _T("\\LocalServer32"));
CString installed_server;
ASSERT1(SUCCEEDED(RegKey::GetValue(local_server_key,
NULL,
&installed_server)));
ASSERT1(!installed_server.IsEmpty());
CString expected_server(app_util::GetModulePath(NULL));
EnclosePath(&expected_server);
ASSERT1(!expected_server.IsEmpty());
SETUP_LOG(L3, (_T("[installed_server=%s][expected_server=%s]"),
installed_server, expected_server));
ASSERT1(installed_server == expected_server);
const GUID proxy_clsid = PROXY_CLSID_IS;
CString ondemand_proxy_clsid_key(base_clsid_key);
ondemand_proxy_clsid_key += GuidToString(proxy_clsid);
CString inproc_server_key(ondemand_proxy_clsid_key + _T("\\InProcServer32"));
ASSERT1(SUCCEEDED(RegKey::GetValue(inproc_server_key,
NULL,
&installed_server)));
ASSERT1(!installed_server.IsEmpty());
expected_server = app_util::GetCurrentModulePath();
ASSERT1(!expected_server.IsEmpty());
SETUP_LOG(L3, (_T("[installed proxy=%s][expected proxy=%s]"),
installed_server, expected_server));
ASSERT1(installed_server == expected_server);
CString igoogleupdate_interface_key(goopdate_utils::GetHKRoot());
igoogleupdate_interface_key += _T("\\Software\\Classes\\Interface\\");
igoogleupdate_interface_key += GuidToString(__uuidof(IGoogleUpdate));
igoogleupdate_interface_key += _T("\\ProxyStubClsid32");
CString proxy_interface_value;
ASSERT1(SUCCEEDED(RegKey::GetValue(igoogleupdate_interface_key,
NULL,
&proxy_interface_value)));
ASSERT1(!proxy_interface_value.IsEmpty());
ASSERT1(proxy_interface_value == GuidToString(proxy_clsid));
#else
UNREFERENCED_PARAMETER(is_machine);
#endif
return S_OK;
}
#endif
HRESULT RegisterOrUnregisterService(bool reg, CString service_path) {
EnclosePath(&service_path);
CommandLineBuilder builder(reg ? COMMANDLINE_MODE_SERVICE_REGISTER :
COMMANDLINE_MODE_SERVICE_UNREGISTER);
CString cmd_line = builder.GetCommandLineArgs();
return RegisterOrUnregisterExe(service_path, cmd_line);
}
} // namespace
SetupGoogleUpdate::SetupGoogleUpdate(bool is_machine)
: is_machine_(is_machine),
extra_code1_(S_OK)
#ifdef _DEBUG
, have_called_uninstall_previous_versions_(false)
#endif
{ // NOLINT
this_version_ = GetVersionString();
}
SetupGoogleUpdate::~SetupGoogleUpdate() {
SETUP_LOG(L2, (_T("[SetupGoogleUpdate::~SetupGoogleUpdate]")));
}
// TODO(omaha): Add a VerifyInstall() method that can be called by /handoff
// instances to verify the installation and call FinishInstall() if it fails.
// Assumes the caller is ensuring this is the only running instance of setup.
// The original process holds the lock while it waits for this one to complete.
HRESULT SetupGoogleUpdate::FinishInstall() {
SETUP_LOG(L2, (_T("[SetupGoogleUpdate::FinishInstall]")));
HRESULT hr = InstallRegistryValues();
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[InstallRegistryValues failed][0x%08x]"), hr));
if (E_ACCESSDENIED == hr) {
return GOOPDATE_E_ACCESSDENIED_SETUP_REG_ACCESS;
}
return hr;
}
hr = InstallLaunchMechanisms();
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[InstallLaunchMechanisms failed][0x%08x]"), hr));
return hr;
}
hr = InstallMsiHelper();
if (FAILED(hr)) {
SETUP_LOG(L1, (_T("[InstallMsiHelper failed][0x%08x]"), hr));
// TODO(omaha): Retry on ERROR_INSTALL_ALREADY_RUNNING like InstallerWrapper
// if we move helper MSI installation after app installation.
ASSERT1(HRESULT_FROM_WIN32(ERROR_INSTALL_SERVICE_FAILURE) == hr ||
HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING) == hr);
}
hr = RegisterOrUnregisterCOMLocalServer(true);
if (FAILED(hr)) {
OPT_LOG(LW, (_T("[RegisterOrUnregisterCOMLocalServer failed][0x%x]"), hr));
// Fall through. Omaha will attempt to install using the in-proc mode.
}
ASSERT1(SUCCEEDED(VerifyCOMLocalServerRegistration(is_machine_)));
// We would prefer to uninstall previous versions last, but the web plugin
// requires that the old plugin is uninstalled before installing the new one.
VERIFY1(SUCCEEDED(UninstallPreviousVersions()));
// Writing this value indicates that this Omaha version was successfully
// installed. This is an artifact of Omaha 2 when pv was set earlier in Setup.
CString reg_update = ConfigManager::Instance()->registry_update(is_machine_);
hr = RegKey::SetValue(reg_update, kRegValueInstalledVersion, this_version_);
if (FAILED(hr)) {
return hr;
}
// Delete the "LastChecked" registry value after a successful install or
// update so that Omaha checks for updates soon after the install. This
// helps detecting a heart beat from the new version sooner as well as
// avoiding deferring application updates for too long in the case where both
// Omaha and application updates are available.
RegKey::DeleteValue(reg_update, kRegValueLastChecked);
return S_OK;
}
// Version values are written at the end of setup, not here.
HRESULT SetupGoogleUpdate::InstallRegistryValues() {
OPT_LOG(L3, (_T("[SetupGoogleUpdate::InstallRegistryValues]")));
const ConfigManager* cm = ConfigManager::Instance();
const TCHAR* keys[] = { cm->registry_google(is_machine_),
cm->registry_update(is_machine_),
cm->registry_client_state(is_machine_),
cm->registry_clients(is_machine_),
cm->registry_clients_goopdate(is_machine_),
cm->registry_client_state_goopdate(is_machine_),
};
HRESULT hr = RegKey::CreateKeys(keys, arraysize(keys));
if (FAILED(hr)) {
OPT_LOG(LE, (_T("[Failed to create reg keys][0x%08x]"), hr));
return hr;
}
if (is_machine_) {
hr = CreateClientStateMedium();
if (FAILED(hr)) {
SETUP_LOG(L3, (_T("[CreateClientStateMedium failed][0x%08x]"), hr));
return hr;
}
}
CString shell_path = goopdate_utils::BuildGoogleUpdateExePath(is_machine_);
if (shell_path.IsEmpty() || !File::Exists(shell_path)) {
SETUP_LOG(LE, (_T("[Failed to get valid shell path]")));
return E_FAIL;
}
hr = RegKey::SetValue(cm->registry_update(is_machine_),
kRegValueInstalledPath,
shell_path);
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[Failed to write shell path][0x%08x]"), hr));
return hr;
}
ASSERT1(!this_version_.IsEmpty());
const CString omaha_clients_key_path =
cm->registry_clients_goopdate(is_machine_);
// Set the version so the constant shell will know which version to use.
// TODO(omaha3): This should be the atomic switch of the version, but it must
// be called before registering the COM servers because GoogleUpdate.exe needs
// the pv to find goopdate.dll. We may need to support rolling this back.
hr = RegKey::SetValue(omaha_clients_key_path,
kRegValueProductVersion,
this_version_);
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[Failed to set version in registry][0x%08x]"), hr));
if (E_ACCESSDENIED == hr) {
return GOOPDATE_E_ACCESSDENIED_SETUP_REG_ACCESS;
}
return hr;
}
// Write Omaha's localized name to the registry. During installation, this
// will use the installation language. For self-updates, it will use the
// user's/Local System's language.
CString omaha_name;
if (!omaha_name.LoadString(IDS_PRODUCT_DISPLAY_NAME)) {
ASSERT1(false);
omaha_name = kAppName;
}
VERIFY1(SUCCEEDED(RegKey::SetValue(omaha_clients_key_path,
kRegValueAppName,
omaha_name)));
// Set pv in ClientState for consistency. Optional, so ignore errors.
const CString omaha_client_state_key_path =
cm->registry_client_state_goopdate(is_machine_);
VERIFY1(SUCCEEDED(RegKey::SetValue(omaha_client_state_key_path,
kRegValueProductVersion,
this_version_)));
if (is_machine_) {
VERIFY1(SUCCEEDED(goopdate_utils::EnableSEHOP(true)));
}
return S_OK;
}
// Creates the ClientStateMedium key and adds ACLs that allows authenticated
// users to read and write values in its subkeys.
// Since this key is not as secure as other keys, the supported values must be
// limited and the use of them must be carefully designed.
HRESULT SetupGoogleUpdate::CreateClientStateMedium() {
ASSERT1(is_machine_);
// Authenticated non-admins may read, write, and create values.
const ACCESS_MASK kNonAdminAccessMask = KEY_READ | KEY_SET_VALUE;
// The override privileges apply to all subkeys and values but not to the
// ClientStateMedium key itself.
const uint8 kAceFlags =
CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE;
const CString key_full_name =
ConfigManager::Instance()->machine_registry_client_state_medium();
HRESULT hr = RegKey::CreateKey(key_full_name);
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[Create ClientStateMedium failed][0x%08x]"), hr));
return hr;
}
// GetNamedSecurityInfo requires the key name start with "MACHINE".
// TODO(omaha): Replace AddAllowedAce or add an override that takes a handle
// instead of a name to eliminate this issue.
CString compatible_key_name = key_full_name;
VERIFY1(1 == compatible_key_name.Replace(MACHINE_KEY_NAME, _T("MACHINE")));
hr = AddAllowedAce(compatible_key_name,
SE_REGISTRY_KEY,
Sids::Interactive(),
kNonAdminAccessMask,
kAceFlags);
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[AddAllowedAce failed][%s][0x%08x]"),
key_full_name, hr));
return hr;
}
return S_OK;
}
HRESULT SetupGoogleUpdate::InstallLaunchMechanisms() {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallLaunchMechanisms]")));
if (is_machine_) {
HRESULT hr = InstallMachineLaunchMechanisms();
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[InstallMachineLaunchMechanisms fail][0x%08x]"), hr));
return hr;
}
} else {
HRESULT hr = InstallUserLaunchMechanisms();
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[InstallUserLaunchMechanisms failed][0x%08x]"), hr));
return hr;
}
}
return S_OK;
}
void SetupGoogleUpdate::UninstallLaunchMechanisms() {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::UninstallLaunchMechanisms]")));
if (is_machine_) {
CString current_dir = app_util::GetModuleDirectory(NULL);
CString service_path = ConcatenatePath(current_dir, kServiceFileName);
VERIFY1(SUCCEEDED(RegisterOrUnregisterService(false, service_path)));
} else {
// We only need to do this in case of the user goopdate, as
// there is no machine Run at startup installation.
VERIFY1(SUCCEEDED(ConfigureUserRunAtStartup(false))); // delete entry
}
VERIFY1(SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(is_machine_)));
}
HRESULT SetupGoogleUpdate::InstallScheduledTask() {
CString exe_path = goopdate_utils::BuildGoogleUpdateExePath(is_machine_);
HighresTimer metrics_timer;
const ULONGLONG install_task_start_ms = metrics_timer.GetElapsedMs();
HRESULT hr = scheduled_task_utils::InstallGoopdateTasks(exe_path,
is_machine_);
if (SUCCEEDED(hr)) {
const ULONGLONG install_task_end_ms = metrics_timer.GetElapsedMs();
ASSERT1(install_task_end_ms >= install_task_start_ms);
metric_setup_install_task_ms.AddSample(
install_task_end_ms - install_task_start_ms);
++metric_setup_install_task_succeeded;
} else {
OPT_LOG(LEVEL_ERROR, (_T("[Install task failed][0x%08x]"), hr));
metric_setup_install_task_error = hr;
}
return hr;
}
// Assumes the any existing service instance has been stopped
// TODO(omaha): Provide service_hr and task_hr failures in a ping.
// They are no longer being provided in the URL.
HRESULT SetupGoogleUpdate::InstallMachineLaunchMechanisms() {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallMachineLaunchMechanisms]")));
++metric_setup_install_service_task_total;
// Install the service and scheduled task. Failing to install both will
// fail setup.
OPT_LOG(L1, (_T("[Installing service]")));
HighresTimer metrics_timer;
HRESULT service_hr = RegisterOrUnregisterService(true,
goopdate_utils::BuildGoogleUpdateExePath(is_machine_));
ASSERT(SUCCEEDED(service_hr), (_T("[registration err][0x%x]"), service_hr));
if (SUCCEEDED(service_hr)) {
metric_setup_install_service_ms.AddSample(
metrics_timer.GetElapsedMs());
++metric_setup_install_service_succeeded;
} else {
metric_setup_install_service_failed_ms.AddSample(
metrics_timer.GetElapsedMs());
OPT_LOG(LEVEL_ERROR, (_T("[Install service failed][0x%08x]"), service_hr));
metric_setup_install_service_error = service_hr;
}
HRESULT task_hr = InstallScheduledTask();
if (FAILED(service_hr) && FAILED(task_hr)) {
++metric_setup_install_service_and_task_failed;
extra_code1_ = task_hr;
return service_hr;
}
// TODO(omaha3): Setup does not know about OEM mode. Figure out a
// different way to do this. Maybe just verify that both are installed.
#if 0
if (args_->is_oem_set) {
// OEM installs are on clean systems in a controlled environment. We expect
// both mechanisms to install.
if (FAILED(service_hr)) {
return service_hr;
}
if (FAILED(task_hr)) {
return task_hr;
}
}
#endif
if (SUCCEEDED(service_hr) && SUCCEEDED(task_hr)) {
++metric_setup_install_service_and_task_succeeded;
}
return S_OK;
}
HRESULT SetupGoogleUpdate::InstallUserLaunchMechanisms() {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallUserLaunchMechanisms]")));
HRESULT run_hr = ConfigureUserRunAtStartup(true); // install
ASSERT(SUCCEEDED(run_hr), (_T("ConfigureRunAtStartup 0x%x"), run_hr));
HRESULT task_hr = InstallScheduledTask();
ASSERT(SUCCEEDED(task_hr), (_T("InstallScheduledTask 0x%x"), task_hr));
if (FAILED(run_hr) && FAILED(task_hr)) {
// We need atleast one launch mechanism.
extra_code1_ = task_hr;
return run_hr;
}
return S_OK;
}
// Sets a value in the Run key in the user registry to start the core.
HRESULT SetupGoogleUpdate::ConfigureUserRunAtStartup(bool install) {
SETUP_LOG(L3, (_T("SetupGoogleUpdate::ConfigureUserRunAtStartup")));
// Always send false argument as this method is only called for user
// goopdate installs.
CString core_cmd = BuildCoreProcessCommandLine();
return ConfigureRunAtStartup(USER_KEY_NAME, kRunValueName, core_cmd, install);
}
HRESULT SetupGoogleUpdate::RegisterOrUnregisterCOMLocalServer(bool reg) {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::RegisterOrUnregisterCOMLocalServer]")
_T("[%d]"), reg));
const CString google_update_path =
goopdate_utils::BuildGoogleUpdateExePath(is_machine_);
CString register_cmd;
register_cmd.Format(_T("/%s"), reg ? kCmdRegServer : kCmdUnregServer);
HRESULT hr = RegisterOrUnregisterExe(google_update_path, register_cmd);
if (FAILED(hr)) {
SETUP_LOG(LE, (_T("[RegisterOrUnregisterExe failed][0x%08x]"), hr));
return hr;
}
return S_OK;
}
// Assumes that the MSI is in the current directory.
// To debug MSI failures, use the following statement:
// ::MsiEnableLog(INSTALLLOGMODE_VERBOSE, _T("C:\\msi.log"), NULL);
HRESULT SetupGoogleUpdate::InstallMsiHelper() {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallMsiHelper]")));
if (!is_machine_) {
return S_OK;
}
++metric_setup_helper_msi_install_total;
HighresTimer metrics_timer;
const CPath msi_path(BuildSupportFileInstallPath(kHelperInstallerName));
ASSERT1(File::Exists(msi_path));
// Setting INSTALLUILEVEL_NONE causes installation to be silent and not
// create a restore point.
::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
// Try a normal install.
UINT res = ::MsiInstallProduct(msi_path, kMsiSuppressAllRebootsCmdLine);
if (ERROR_PRODUCT_VERSION == res) {
// The product may already be installed. Force a reinstall of everything.
SETUP_LOG(L3, (_T("[ERROR_PRODUCT_VERSION returned - forcing reinstall]")));
CString force_install_cmd_line;
force_install_cmd_line.Format(_T("REINSTALL=ALL REINSTALLMODE=vamus %s"),
kMsiSuppressAllRebootsCmdLine);
res = ::MsiInstallProduct(msi_path, force_install_cmd_line);
}
HRESULT hr = HRESULT_FROM_WIN32(res);
if (FAILED(hr)) {
SETUP_LOG(L1, (_T("[MsiInstallProduct failed][0x%08x][%u]"), hr, res));
return hr;
}
metric_setup_helper_msi_install_ms.AddSample(metrics_timer.GetElapsedMs());
++metric_setup_helper_msi_install_succeeded;
return S_OK;
}
// The MSI is uninstalled.
// TODO(omaha): Make sure this works after deleting the MSI.
HRESULT SetupGoogleUpdate::UninstallMsiHelper() {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::UninstallMsiHelper]")));
if (!is_machine_) {
return S_OK;
}
// Setting INSTALLUILEVEL_NONE causes installation to be silent and not
// create a restore point.
::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
// MSDN says that eInstallState must be INSTALLSTATE_DEFAULT in order for the
// command line to be used. Therefore, instead of using INSTALLSTATE_ABSENT
// to uninstall, we must pass REMOVE=ALL in the command line.
CString uninstall_cmd_line;
uninstall_cmd_line.Format(_T("REMOVE=ALL %s"),
kMsiSuppressAllRebootsCmdLine);
UINT res = ::MsiConfigureProductEx(kHelperInstallerProductGuid,
INSTALLLEVEL_DEFAULT,
INSTALLSTATE_DEFAULT,
uninstall_cmd_line);
// Ignore the product not currently installed result.
if ((ERROR_SUCCESS != res) && (ERROR_UNKNOWN_PRODUCT != res)) {
HRESULT hr = HRESULT_FROM_WIN32(res);
if (FAILED(hr)) {
SETUP_LOG(L1, (_T("[MsiInstallProduct failed][0x%08x][%u]"), hr, res));
return hr;
}
}
return S_OK;
}
HRESULT SetupGoogleUpdate::InstallBrowserPlugins() {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallBrowserPlugins]")));
ASSERT1(have_called_uninstall_previous_versions_);
// Failure of registration of optional components is acceptable in release
// builds.
HRESULT hr = S_OK;
CString plugin_path =
BuildSupportFileInstallPath(UPDATE_PLUGIN_FILENAME);
hr = RegisterDll(plugin_path);
if (FAILED(hr)) {
SETUP_LOG(L1, (_T("[Register plugin DLL failed][0x%08x]"), hr));
}
// TODO(omaha): Enable when we ship the BHO.
#if 0
// Only install the BHO for machine installs. There is no corresponding HKCU
// registration for BHOs.
if (is_machine_) {
CString goopdate_bho_proxy_path = BuildSupportFileInstallPath(BHO_FILENAME);
hr = RegisterDll(goopdate_bho_proxy_path);
if (FAILED(hr)) {
SETUP_LOG(L1, (_T("[Register bho_proxy DLL failed][0x%08x]"), hr));
}
}
#endif
return hr;
}
HRESULT SetupGoogleUpdate::UninstallBrowserPlugins() {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::UninstallBrowserPlugins]")));
// Unregistration. Failure is acceptable in release builds.
HRESULT hr = S_OK;
CString plugin_path =
BuildSupportFileInstallPath(UPDATE_PLUGIN_FILENAME);
hr = UnregisterDll(plugin_path);
if (FAILED(hr)) {
SETUP_LOG(L1, (_T("[Unregister plugin DLL failed][0x%08x]"), hr));
}
// TODO(omaha): Enable when we ship the BHO.
#if 0
if (is_machine_) {
CString goopdate_bho_proxy_path = BuildSupportFileInstallPath(BHO_FILENAME);
hr = UnregisterDll(goopdate_bho_proxy_path);
if (FAILED(hr)) {
SETUP_LOG(L1, (_T("[Unregister bho_proxy DLL failed][0x%08x]"), hr));
}
}
#endif
return hr;
}
CString SetupGoogleUpdate::BuildSupportFileInstallPath(
const CString& filename) const {
SETUP_LOG(L3, (_T("[SetupGoogleUpdate::BuildSupportFileInstallPath][%s]"),
filename));
CPath install_file_path = goopdate_utils::BuildInstallDirectory(
is_machine_,
this_version_);
VERIFY1(install_file_path.Append(filename));
return install_file_path;
}
CString SetupGoogleUpdate::BuildCoreProcessCommandLine() const {
CString google_update_path =
goopdate_utils::BuildGoogleUpdateExePath(is_machine_);
CommandLineBuilder builder(COMMANDLINE_MODE_CORE);
return builder.GetCommandLine(google_update_path);
}
HRESULT SetupGoogleUpdate::UninstallPreviousVersions() {
#ifdef _DEBUG
have_called_uninstall_previous_versions_ = true;
#endif
VERIFY1(SUCCEEDED(
scheduled_task_utils::UninstallLegacyGoopdateTasks(is_machine_)));
CString install_path(
is_machine_ ? ConfigManager::Instance()->GetMachineGoopdateInstallDir() :
ConfigManager::Instance()->GetUserGoopdateInstallDir());
SETUP_LOG(L1, (_T("[SetupGoogleUpdate::UninstallPreviousVersions][%s][%s]"),
install_path, this_version_));
// An empty install_path can be disastrous as it will start deleting from the
// current directory.
ASSERT1(!install_path.IsEmpty());
if (install_path.IsEmpty()) {
return E_UNEXPECTED;
}
// In the Google\\Update directory, run over all files and directories.
WIN32_FIND_DATA file_data = {0};
CPath find_files(install_path);
VERIFY1(find_files.Append(_T("*.*")));
scoped_hfind find_handle(::FindFirstFile(find_files, &file_data));
ASSERT1(find_handle);
if (!find_handle) {
// We should have found ".", "..", and our versioned directory.
DWORD err = ::GetLastError();
SETUP_LOG(LE, (L"[Subdirs not found under dir][%s][%d]", find_files, err));
return HRESULT_FROM_WIN32(err);
}
// Find the rightmost path element of the download directory.
CPath download_dir(OMAHA_REL_DOWNLOAD_STORAGE_DIR);
download_dir.StripPath();
BOOL found_next = TRUE;
for (; found_next; found_next = ::FindNextFile(get(find_handle),
&file_data)) {
CPath file_or_directory(install_path);
VERIFY1(file_or_directory.Append(file_data.cFileName));
if (!(file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
// Do not delete the shell as it is used by all versions.
if (_tcsicmp(file_data.cFileName, kOmahaShellFileName)) {
DeleteBeforeOrAfterReboot(file_or_directory);
}
} else if (_tcscmp(file_data.cFileName, _T("..")) &&
_tcscmp(file_data.cFileName, _T(".")) &&
_tcsicmp(file_data.cFileName, this_version_) &&
_tcsicmp(file_data.cFileName, download_dir)) {
// Unregister the previous version OneClick if it exists. Ignore
// failures. The file is named npGoogleOneClick*.dll.
CPath old_oneclick(file_or_directory);
VERIFY1(old_oneclick.Append(ONECLICK_PLUGIN_NAME _T("*.dll")));
WIN32_FIND_DATA old_oneclick_file_data = {};
scoped_hfind found_oneclick(::FindFirstFile(old_oneclick,
&old_oneclick_file_data));
if (found_oneclick) {
CPath old_oneclick_file(file_or_directory);
VERIFY1(old_oneclick_file.Append(old_oneclick_file_data.cFileName));
VERIFY1(SUCCEEDED(UnregisterDll(old_oneclick_file)));
}
// Unregister the previous version of the plugin if it exists. Ignore
// failures. The file is named npGoogleUpdate*.dll.
CPath old_plugin(file_or_directory);
VERIFY1(old_plugin.Append(UPDATE_PLUGIN_NAME _T("*.dll")));
WIN32_FIND_DATA old_plugin_file_data = {};
scoped_hfind found_plugin(::FindFirstFile(old_plugin,
&old_plugin_file_data));
if (found_plugin) {
CPath old_plugin_file(file_or_directory);
VERIFY1(old_plugin_file.Append(old_plugin_file_data.cFileName));
VERIFY1(SUCCEEDED(UnregisterDll(old_plugin_file)));
}
if (is_machine_) {
// TODO(omaha): Enable when we ship the BHO.
/*
// BHO is only installed for the machine case.
// Unregister the previous version BHO if it exists. Ignore failures.
CPath old_bho_dll(file_or_directory);
VERIFY1(old_bho_dll.Append(BHO_FILENAME));
if (File::Exists(old_bho_dll)) {
if (FAILED(UnregisterDll(old_bho_dll))) {
SETUP_LOG(LW, (L"[UnregisterDll() failed][%s]", old_bho_dll));
}
}
*/
}
// Delete entire sub-directory.
DeleteBeforeOrAfterReboot(file_or_directory);
}
}
if (!found_next) {
DWORD err = ::GetLastError();
if (ERROR_NO_MORE_FILES != err) {
SETUP_LOG(LE, (L"[::FindNextFile() failed][%d]", err));
return HRESULT_FROM_WIN32(err);
}
}
// Clean up existing machine ID and user ID since they are no longer used.
// Ignore failures as they may not be present and we may not have permission
// to HKLM.
const TCHAR* const kRegValueMachineId = _T("mi");
const TCHAR* const kRegValueUserId = _T("ui");
RegKey::DeleteValue(ConfigManager::Instance()->machine_registry_update(),
kRegValueMachineId);
RegKey::DeleteValue(
ConfigManager::Instance()->registry_update(is_machine_),
kRegValueUserId);
return S_OK;
}
void SetupGoogleUpdate::Uninstall() {
OPT_LOG(L1, (_T("[SetupGoogleUpdate::Uninstall]")));
HRESULT hr = UninstallBrowserPlugins();
if (FAILED(hr)) {
SETUP_LOG(LW, (_T("[UninstallBrowserPlugins failed][0x%08x]"), hr));
ASSERT1(HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND) == hr);
}
// If running from the installed location instead of a temporary location,
// we assume that Omaha had been properly installed and can verify the COM
// registration.
if (goopdate_utils::IsRunningFromOfficialGoopdateDir(is_machine_)) {
ASSERT1(SUCCEEDED(VerifyCOMLocalServerRegistration(is_machine_)));
}
hr = RegisterOrUnregisterCOMLocalServer(false);
if (FAILED(hr)) {
SETUP_LOG(LW,
(_T("[RegisterOrUnregisterCOMLocalServer failed][0x%08x]"), hr));
ASSERT1(GOOGLEUPDATE_E_DLL_NOT_FOUND == hr ||
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
}
hr = UninstallMsiHelper();
if (FAILED(hr)) {
SETUP_LOG(L1, (_T("[UninstallMsiHelper failed][0x%08x]"), hr));
ASSERT1(HRESULT_FROM_WIN32(ERROR_INSTALL_SERVICE_FAILURE) == hr);
}
UninstallLaunchMechanisms();
// Remove everything under top level Google Update registry key.
hr = DeleteRegistryKeys();
ASSERT1(SUCCEEDED(hr) || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
}
// Also deletes the main Google Update key if there is nothing in it.
HRESULT SetupGoogleUpdate::DeleteRegistryKeys() {
OPT_LOG(L3, (_T("[SetupGoogleUpdate::DeleteRegistryKeys]")));
if (is_machine_) {
VERIFY1(SUCCEEDED(goopdate_utils::EnableSEHOP(false)));
}
CString root_key = ConfigManager::Instance()->registry_update(is_machine_);
ASSERT1(!root_key.IsEmpty());
RegKey root;
HRESULT hr = root.Open(root_key);
if (FAILED(hr)) {
return hr;
}
std::vector<CString> sub_keys;
size_t num_keys = root.GetSubkeyCount();
for (size_t i = 0; i < num_keys; ++i) {
CString sub_key_name;
hr = root.GetSubkeyNameAt(i, &sub_key_name);
ASSERT1(hr == S_OK);
if (SUCCEEDED(hr)) {
sub_keys.push_back(sub_key_name);
}
}
ASSERT1(num_keys == sub_keys.size());
// Delete all the sub keys of the root key.
for (size_t i = 0; i < num_keys; ++i) {
VERIFY1(SUCCEEDED(root.RecurseDeleteSubKey(sub_keys[i])));
}
// Now delete all the values of the root key.
// The Last* values are not deleted.
// TODO(omaha3): The above is a temporary fix for bug 1539293. Need a better
// long-term solution in Omaha 3.
size_t num_values = root.GetValueCount();
std::vector<CString> value_names;
for (size_t i = 0; i < num_values; ++i) {
CString value_name;
DWORD type = 0;
hr = root.GetValueNameAt(i, &value_name, &type);
ASSERT1(hr == S_OK);
// TODO(omaha): Remove kRegValueLast* once we have an install API.
if (SUCCEEDED(hr)) {
if (value_name != kRegValueUserId &&
value_name != kRegValueLastInstallerResult &&
value_name != kRegValueLastInstallerError &&
value_name != kRegValueLastInstallerExtraCode1 &&
value_name != kRegValueLastInstallerResultUIString &&
value_name != kRegValueLastInstallerSuccessLaunchCmdLine) {
value_names.push_back(value_name);
}
}
}
for (size_t i = 0; i < value_names.size(); ++i) {
VERIFY1(SUCCEEDED(root.DeleteValue(value_names[i])));
}
if (0 == root.GetValueCount() && 0 == root.GetSubkeyCount()) {
VERIFY1(SUCCEEDED(RegKey::DeleteKey(root_key, false)));
}
return S_OK;
}
} // namespace omaha