Chromium Code Reviews| Index: remoting/host/plugin/daemon_installer_win.cc |
| diff --git a/remoting/host/plugin/daemon_installer_win.cc b/remoting/host/plugin/daemon_installer_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8d4cb38a36b3144432efe9ce2a3a50cd9d0f9873 |
| --- /dev/null |
| +++ b/remoting/host/plugin/daemon_installer_win.cc |
| @@ -0,0 +1,354 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "remoting/host/plugin/daemon_installer_win.h" |
| + |
| +#include <windows.h> |
| + |
| +#include "base/string16.h" |
| +#include "base/bind.h" |
| +#include "base/message_loop.h" |
| +#include "base/process_util.h" |
| +#include "base/stringprintf.h" |
| +#include "base/time.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "base/win/object_watcher.h" |
| +#include "base/win/registry.h" |
| +#include "base/win/scoped_bstr.h" |
| +#include "base/win/scoped_comptr.h" |
| +#include "base/win/scoped_handle.h" |
| + |
| +namespace omaha { |
| +#include "google_update/google_update_idl.h" |
| +}; |
| + |
| +using base::win::ScopedBstr; |
| +using base::win::ScopedComPtr; |
| + |
| +namespace { |
| + |
| +// The COM elevation moniker for Omaha. |
| +const char kOmahaElevationMoniker[] = "Elevation:Administrator!new:" |
| + "GoogleUpdate.Update3WebMachine"; |
| + |
| +// The registry key where the configuration of Omaha is stored. |
| +const char kOmahaUpdateKeyName[] = "Software\\Google\\Update"; |
| + |
| +// The name of the value where the full path to GoogleUpdate.exe is stored. |
| +const char kOmahaPathValueName[] = "path"; |
| + |
| +// The command line format string for GoogleUpdate.exe |
| +const char kGoogleUpdateCommandLineFormat[] = |
| + "\"%ls\" /install \"bundlename=Chromoting%%20Host&appguid=%ls&" |
| + "appname=Chromoting%%20Host&needsadmin=True&lang=%ls\""; |
| + |
| +// The Omaha Appid of the host. |
| +const char kOmahaAppid[] = "{b210701e-ffc4-49e3-932b-370728c72662}"; |
| + |
| +// TODO(alexeypa): Get the desired laungage from the web app. |
| +const char kOmahaLanguage[] = "en"; |
| + |
| +// An empty string for optional parameters. |
| +const char kOmahaEmpty[] = ""; |
| + |
| +// The installation status polling interval. |
| +const int kOmahaPollIntervalMs = 500; |
| + |
| +} // namespace |
| + |
| +namespace remoting { |
| + |
| +// This class implements on-demand installation of the Chromoting Host via |
| +// per-machine Omaha instance. |
| +class DaemonComInstallerWin : public DaemonInstallerWin { |
| + public: |
| + DaemonComInstallerWin(const ScopedComPtr<omaha::IGoogleUpdate3Web>& update3); |
| + |
| + // DaemonInstallerWin implementation. |
| + virtual void Install(const CompletionCallback& done) OVERRIDE; |
| + |
| + private: |
| + // Polls the installation status performing state-specific actions (such as |
| + // starting installation once download has finished). |
| + void PollInstallationStatus(const CompletionCallback& done); |
| + |
| + ScopedComPtr<omaha::IGoogleUpdate3Web> update3_; |
| + ScopedComPtr<omaha::IAppBundleWeb> bundle_; |
| + ScopedComPtr<omaha::IAppWeb> app_; |
| +}; |
| + |
| +// This class implements on-demand installation of the Chromoting Host by |
| +// launching a per-user instance of Omaha and requesting elevation. |
| +class DaemonCommandLineInstallerWin |
| + : public DaemonInstallerWin, |
| + public base::win::ObjectWatcher::Delegate { |
| +public: |
|
Sergey Ulanov
2012/04/07 18:09:21
nit: indent with one space.
alexeypa (please no reviews)
2012/04/09 20:24:11
Done.
|
| + DaemonCommandLineInstallerWin(); |
| + |
| + // DaemonInstallerWin implementation. |
| + virtual void Install(const CompletionCallback& done) OVERRIDE; |
| + |
| + // base::win::ObjectWatcher::Delegate implementation. |
| + virtual void OnObjectSignaled(HANDLE object) OVERRIDE; |
| + |
| + private: |
| + CompletionCallback done_; |
| + |
| + // Handle of the launched process. |
| + base::win::ScopedHandle process_; |
| + |
| + // Used to determine when the launched process terminates. |
| + base::win::ObjectWatcher process_watcher_; |
| +}; |
| + |
| +DaemonComInstallerWin::DaemonComInstallerWin( |
| + const ScopedComPtr<omaha::IGoogleUpdate3Web>& update3) |
| + : update3_(update3) { |
| +} |
| + |
| +void DaemonComInstallerWin::Install(const CompletionCallback& done) { |
| + // Create an app bundle. |
| + ScopedComPtr<IDispatch> dispatch; |
| + HRESULT hr = update3_->createAppBundleWeb(dispatch.Receive()); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + hr = dispatch.QueryInterface(omaha::IID_IAppBundleWeb, |
| + bundle_.ReceiveVoid()); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + hr = bundle_->initialize(); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + // Add Chromoting Host to the bundle. |
| + ScopedBstr appid(ASCIIToUTF16(kOmahaAppid).c_str()); |
| + ScopedBstr empty(ASCIIToUTF16(kOmahaEmpty).c_str()); |
|
Sergey Ulanov
2012/04/07 18:09:21
Do we need to convert and empty string? Can this b
alexeypa (please no reviews)
2012/04/09 20:24:11
This was needed because of the presubmit check fro
|
| + ScopedBstr language(ASCIIToUTF16(kOmahaLanguage).c_str()); |
| + hr = bundle_->createApp(appid, empty, language, empty); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + hr = bundle_->checkForUpdate(); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + dispatch.Release(); |
| + hr = bundle_->get_appWeb(0, dispatch.Receive()); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + hr = dispatch.QueryInterface(omaha::IID_IAppWeb, |
| + app_.ReceiveVoid()); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + // Now poll for the installation status. |
| + PollInstallationStatus(done); |
| +} |
| + |
| +void DaemonComInstallerWin::PollInstallationStatus( |
| + const CompletionCallback& done) { |
| + // Get the current application installation state. |
| + // N.B. The object underlying the ICurrentState interface has static data that |
| + // does not get updated as the server state changes. To get the most "current" |
| + // state, the currentState property needs to be queried again. |
| + ScopedComPtr<IDispatch> dispatch; |
| + HRESULT hr = app_->get_currentState(dispatch.Receive()); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + ScopedComPtr<omaha::ICurrentState> current_state; |
| + hr = dispatch.QueryInterface(omaha::IID_ICurrentState, |
| + current_state.ReceiveVoid()); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + LONG state; |
| + hr = current_state->get_stateValue(&state); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + |
| + // Perform state-specific actions. |
| + switch (state) { |
| + case omaha::STATE_INIT: |
| + case omaha::STATE_WAITING_TO_CHECK_FOR_UPDATE: |
| + case omaha::STATE_CHECKING_FOR_UPDATE: |
| + case omaha::STATE_WAITING_TO_DOWNLOAD: |
| + case omaha::STATE_RETRYING_DOWNLOAD: |
| + case omaha::STATE_DOWNLOADING: |
| + case omaha::STATE_WAITING_TO_INSTALL: |
| + case omaha::STATE_INSTALLING: |
| + case omaha::STATE_PAUSED: |
| + break; |
| + |
| + case omaha::STATE_UPDATE_AVAILABLE: |
| + hr = bundle_->download(); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + break; |
| + |
| + case omaha::STATE_DOWNLOAD_COMPLETE: |
| + case omaha::STATE_EXTRACTING: |
| + case omaha::STATE_APPLYING_DIFFERENTIAL_PATCH: |
| + case omaha::STATE_READY_TO_INSTALL: |
| + hr = bundle_->install(); |
| + if (FAILED(hr)) { |
| + done.Run(hr); |
| + return; |
| + } |
| + break; |
| + |
| + case omaha::STATE_INSTALL_COMPLETE: |
| + case omaha::STATE_NO_UPDATE: |
| + // Installation complete or not required. Report success. |
| + done.Run(S_OK); |
| + return; |
| + |
| + case omaha::STATE_ERROR: { |
| + HRESULT error_code; |
| + hr = current_state->get_errorCode(&error_code); |
| + if (FAILED(hr)) { |
| + error_code = hr; |
| + } |
| + done.Run(error_code); |
| + return; |
| + } |
| + |
| + default: |
| + LOG(ERROR) << "Unknown bundle state: " << state << "."; |
| + done.Run(E_FAIL); |
| + return; |
| + } |
| + |
| + // Keep polling. |
| + MessageLoop::current()->PostDelayedTask( |
|
Sergey Ulanov
2012/04/07 18:09:21
use base::RepeatingTimer instead of posting repeat
alexeypa (please no reviews)
2012/04/09 20:24:11
Done.
|
| + FROM_HERE, |
| + base::Bind(&DaemonComInstallerWin::PollInstallationStatus, |
| + base::Unretained(this), done), |
|
Sergey Ulanov
2012/04/07 18:09:21
not sure if base::Unretained() is safe here. This
alexeypa (please no reviews)
2012/04/09 20:24:11
Done.
|
| + base::TimeDelta::FromMilliseconds(kOmahaPollIntervalMs)); |
| +} |
| + |
| +DaemonCommandLineInstallerWin::DaemonCommandLineInstallerWin() { |
| + process_watcher_.StopWatching(); |
| +} |
| + |
| +void DaemonCommandLineInstallerWin::Install(const CompletionCallback& done) { |
| + // Get the full path to GoogleUpdate.exe from the registry. |
| + base::win::RegKey update_key; |
| + LONG result = update_key.Open(HKEY_CURRENT_USER, |
| + ASCIIToUTF16(kOmahaUpdateKeyName).c_str(), |
| + KEY_READ); |
| + if (result != ERROR_SUCCESS) { |
| + done.Run(HRESULT_FROM_WIN32(result)); |
| + return; |
| + } |
| + |
| + string16 google_update; |
| + result = update_key.ReadValue(ASCIIToUTF16(kOmahaPathValueName).c_str(), |
| + &google_update); |
| + if (result != ERROR_SUCCESS) { |
| + done.Run(HRESULT_FROM_WIN32(result)); |
| + return; |
| + } |
| + |
| + // Launch the updater process and wait for its termination. |
| + string16 command_line = |
| + StringPrintf(ASCIIToUTF16(kGoogleUpdateCommandLineFormat).c_str(), |
| + google_update.c_str(), |
| + ASCIIToUTF16(kOmahaAppid).c_str(), |
| + ASCIIToUTF16(kOmahaLanguage).c_str()); |
| + |
| + base::LaunchOptions options; |
| + if (!base::LaunchProcess(command_line, options, process_.Receive())) { |
| + result = GetLastError(); |
| + done.Run(HRESULT_FROM_WIN32(result)); |
| + return; |
| + } |
| + |
| + if (!process_watcher_.StartWatching(process_.Get(), this)) { |
| + result = GetLastError(); |
| + done.Run(HRESULT_FROM_WIN32(result)); |
| + return; |
| + } |
| + |
| + done_ = done; |
| +} |
| + |
| +void DaemonCommandLineInstallerWin::OnObjectSignaled(HANDLE object) { |
| + // Check if the updater process returned success. |
| + DWORD exit_code; |
| + if (GetExitCodeProcess(process_.Get(), &exit_code) && exit_code == 0) { |
| + done_.Run(S_OK); |
| + } else { |
| + done_.Run(E_FAIL); |
| + } |
| +} |
| + |
| +DaemonInstallerWin::DaemonInstallerWin() { |
| +} |
| + |
| +DaemonInstallerWin::~DaemonInstallerWin() { |
| +} |
| + |
| +// static |
| +HRESULT DaemonInstallerWin::Create( |
| + scoped_ptr<DaemonInstallerWin>* installer_out) { |
| + // Check if the machine instance of Omaha is available. |
| + BIND_OPTS3 bind_options; |
| + memset(&bind_options, 0, sizeof(bind_options)); |
| + bind_options.cbStruct = sizeof(bind_options); |
| + bind_options.hwnd = NULL; |
| + bind_options.dwClassContext = CLSCTX_LOCAL_SERVER; |
| + |
| + ScopedComPtr<omaha::IGoogleUpdate3Web> update3; |
| + HRESULT hr = ::CoGetObject( |
| + ASCIIToUTF16(kOmahaElevationMoniker).c_str(), |
| + &bind_options, |
| + omaha::IID_IGoogleUpdate3Web, |
| + update3.ReceiveVoid()); |
| + if (SUCCEEDED(hr)) { |
| + // The machine instance of Omaha is available and we successfully passed |
| + // the UAC prompt. |
| + *installer_out = |
| + scoped_ptr<DaemonInstallerWin>(new DaemonComInstallerWin(update3)); |
| + return S_OK; |
| + } else if (hr == CO_E_CLASSSTRING) { |
| + // The machine instance of Omaha is not available so we will have to run |
| + // GoogleUpdate.exe manually passing "needsadmin=True". This will cause |
| + // Omaha to install the machine instance first and then install Chromoting |
| + // Host. |
| + *installer_out = |
| + scoped_ptr<DaemonInstallerWin>(new DaemonCommandLineInstallerWin()); |
| + return S_OK; |
| + } else { |
| + // The user declined the UAC prompt or some other error occured. |
| + return hr; |
| + } |
| +} |
| + |
| +} // namespace remoting |