| 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..9d7e524f81f9362b25fdacac4f9b9e0b93563531
|
| --- /dev/null
|
| +++ b/remoting/host/plugin/daemon_installer_win.cc
|
| @@ -0,0 +1,368 @@
|
| +// 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/bind.h"
|
| +#include "base/message_loop.h"
|
| +#include "base/process_util.h"
|
| +#include "base/string16.h"
|
| +#include "base/stringize_macros.h"
|
| +#include "base/stringprintf.h"
|
| +#include "base/time.h"
|
| +#include "base/timer.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"
|
| +} // namespace omaha
|
| +
|
| +using base::win::ScopedBstr;
|
| +using base::win::ScopedComPtr;
|
| +
|
| +namespace {
|
| +
|
| +// The COM elevation moniker for Omaha.
|
| +const char16 kOmahaElevationMoniker[] =
|
| + TO_L_STRING("Elevation:Administrator!new:GoogleUpdate.Update3WebMachine");
|
| +
|
| +// The registry key where the configuration of Omaha is stored.
|
| +const char16 kOmahaUpdateKeyName[] = TO_L_STRING("Software\\Google\\Update");
|
| +
|
| +// The name of the value where the full path to GoogleUpdate.exe is stored.
|
| +const char16 kOmahaPathValueName[] = TO_L_STRING("path");
|
| +
|
| +// The command line format string for GoogleUpdate.exe
|
| +const char16 kGoogleUpdateCommandLineFormat[] =
|
| + TO_L_STRING("\"%ls\" /install \"bundlename=Chromoting%%20Host&appguid=%ls&")
|
| + TO_L_STRING("appname=Chromoting%%20Host&needsadmin=True&lang=%ls\"");
|
| +
|
| +// The Omaha Appid of the host.
|
| +const char16 kOmahaAppid[] =
|
| + TO_L_STRING("{b210701e-ffc4-49e3-932b-370728c72662}");
|
| +
|
| +// TODO(alexeypa): Get the desired laungage from the web app.
|
| +const char16 kOmahaLanguage[] = TO_L_STRING("en");
|
| +
|
| +// An empty string for optional parameters.
|
| +const char16 kOmahaEmpty[] = TO_L_STRING("");
|
| +
|
| +// 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,
|
| + const CompletionCallback& done);
|
| +
|
| + // DaemonInstallerWin implementation.
|
| + virtual void Install() OVERRIDE;
|
| +
|
| + private:
|
| + // Polls the installation status performing state-specific actions (such as
|
| + // starting installation once download has finished).
|
| + void PollInstallationStatus();
|
| +
|
| + // Omaha interfaces.
|
| + ScopedComPtr<omaha::IAppWeb> app_;
|
| + ScopedComPtr<omaha::IAppBundleWeb> bundle_;
|
| + ScopedComPtr<omaha::IGoogleUpdate3Web> update3_;
|
| +
|
| + base::Timer polling_timer_;
|
| +};
|
| +
|
| +// 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:
|
| + DaemonCommandLineInstallerWin(const CompletionCallback& done);
|
| + ~DaemonCommandLineInstallerWin();
|
| +
|
| + // DaemonInstallerWin implementation.
|
| + virtual void Install() OVERRIDE;
|
| +
|
| + // base::win::ObjectWatcher::Delegate implementation.
|
| + virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
|
| +
|
| + private:
|
| + // 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,
|
| + const CompletionCallback& done)
|
| + : DaemonInstallerWin(done),
|
| + update3_(update3),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(
|
| + polling_timer_(
|
| + FROM_HERE,
|
| + base::TimeDelta::FromMilliseconds(kOmahaPollIntervalMs),
|
| + base::Bind(&DaemonComInstallerWin::PollInstallationStatus,
|
| + base::Unretained(this)),
|
| + false)) {
|
| +}
|
| +
|
| +void DaemonComInstallerWin::Install() {
|
| + // Create an app bundle.
|
| + ScopedComPtr<IDispatch> dispatch;
|
| + HRESULT hr = update3_->createAppBundleWeb(dispatch.Receive());
|
| + if (FAILED(hr)) {
|
| + Done(hr);
|
| + return;
|
| + }
|
| +
|
| + hr = dispatch.QueryInterface(omaha::IID_IAppBundleWeb, bundle_.ReceiveVoid());
|
| + if (FAILED(hr)) {
|
| + Done(hr);
|
| + return;
|
| + }
|
| +
|
| + hr = bundle_->initialize();
|
| + if (FAILED(hr)) {
|
| + Done(hr);
|
| + return;
|
| + }
|
| +
|
| + // Add Chromoting Host to the bundle.
|
| + ScopedBstr appid(kOmahaAppid);
|
| + ScopedBstr empty(kOmahaEmpty);
|
| + ScopedBstr language(kOmahaLanguage);
|
| + hr = bundle_->createApp(appid, empty, language, empty);
|
| + if (FAILED(hr)) {
|
| + Done(hr);
|
| + return;
|
| + }
|
| +
|
| + hr = bundle_->checkForUpdate();
|
| + if (FAILED(hr)) {
|
| + Done(hr);
|
| + return;
|
| + }
|
| +
|
| + dispatch.Release();
|
| + hr = bundle_->get_appWeb(0, dispatch.Receive());
|
| + if (FAILED(hr)) {
|
| + Done(hr);
|
| + return;
|
| + }
|
| +
|
| + hr = dispatch.QueryInterface(omaha::IID_IAppWeb,
|
| + app_.ReceiveVoid());
|
| + if (FAILED(hr)) {
|
| + Done(hr);
|
| + return;
|
| + }
|
| +
|
| + // Now poll for the installation status.
|
| + PollInstallationStatus();
|
| +}
|
| +
|
| +void DaemonComInstallerWin::PollInstallationStatus() {
|
| + // 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(hr);
|
| + return;
|
| + }
|
| +
|
| + ScopedComPtr<omaha::ICurrentState> current_state;
|
| + hr = dispatch.QueryInterface(omaha::IID_ICurrentState,
|
| + current_state.ReceiveVoid());
|
| + if (FAILED(hr)) {
|
| + Done(hr);
|
| + return;
|
| + }
|
| +
|
| + LONG state;
|
| + hr = current_state->get_stateValue(&state);
|
| + if (FAILED(hr)) {
|
| + Done(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(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(hr);
|
| + return;
|
| + }
|
| + break;
|
| +
|
| + case omaha::STATE_INSTALL_COMPLETE:
|
| + case omaha::STATE_NO_UPDATE:
|
| + // Installation complete or not required. Report success.
|
| + Done(S_OK);
|
| + return;
|
| +
|
| + case omaha::STATE_ERROR: {
|
| + HRESULT error_code;
|
| + hr = current_state->get_errorCode(&error_code);
|
| + if (FAILED(hr)) {
|
| + error_code = hr;
|
| + }
|
| + Done(error_code);
|
| + return;
|
| + }
|
| +
|
| + default:
|
| + LOG(ERROR) << "Unknown bundle state: " << state << ".";
|
| + Done(E_FAIL);
|
| + return;
|
| + }
|
| +
|
| + // Keep polling.
|
| + polling_timer_.Reset();
|
| +}
|
| +
|
| +DaemonCommandLineInstallerWin::DaemonCommandLineInstallerWin(
|
| + const CompletionCallback& done) : DaemonInstallerWin(done) {
|
| +}
|
| +
|
| +DaemonCommandLineInstallerWin::~DaemonCommandLineInstallerWin() {
|
| + process_watcher_.StopWatching();
|
| +}
|
| +
|
| +void DaemonCommandLineInstallerWin::Install() {
|
| + // Get the full path to GoogleUpdate.exe from the registry.
|
| + base::win::RegKey update_key;
|
| + LONG result = update_key.Open(HKEY_CURRENT_USER,
|
| + kOmahaUpdateKeyName,
|
| + KEY_READ);
|
| + if (result != ERROR_SUCCESS) {
|
| + Done(HRESULT_FROM_WIN32(result));
|
| + return;
|
| + }
|
| +
|
| + string16 google_update;
|
| + result = update_key.ReadValue(kOmahaPathValueName,
|
| + &google_update);
|
| + if (result != ERROR_SUCCESS) {
|
| + Done(HRESULT_FROM_WIN32(result));
|
| + return;
|
| + }
|
| +
|
| + // Launch the updater process and wait for its termination.
|
| + string16 command_line =
|
| + StringPrintf(kGoogleUpdateCommandLineFormat,
|
| + google_update.c_str(),
|
| + kOmahaAppid,
|
| + kOmahaLanguage);
|
| +
|
| + base::LaunchOptions options;
|
| + if (!base::LaunchProcess(command_line, options, process_.Receive())) {
|
| + result = GetLastError();
|
| + Done(HRESULT_FROM_WIN32(result));
|
| + return;
|
| + }
|
| +
|
| + if (!process_watcher_.StartWatching(process_.Get(), this)) {
|
| + result = GetLastError();
|
| + Done(HRESULT_FROM_WIN32(result));
|
| + return;
|
| + }
|
| +}
|
| +
|
| +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(S_OK);
|
| + } else {
|
| + Done(E_FAIL);
|
| + }
|
| +}
|
| +
|
| +DaemonInstallerWin::DaemonInstallerWin(const CompletionCallback& done)
|
| + : done_(done) {
|
| +}
|
| +
|
| +DaemonInstallerWin::~DaemonInstallerWin() {
|
| +}
|
| +
|
| +void DaemonInstallerWin::Done(HRESULT result) {
|
| + done_.Run(result);
|
| +}
|
| +
|
| +// static
|
| +scoped_ptr<DaemonInstallerWin> DaemonInstallerWin::Create(
|
| + CompletionCallback done) {
|
| + // 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 result = ::CoGetObject(
|
| + kOmahaElevationMoniker,
|
| + &bind_options,
|
| + omaha::IID_IGoogleUpdate3Web,
|
| + update3.ReceiveVoid());
|
| + if (SUCCEEDED(result)) {
|
| + // The machine instance of Omaha is available and we successfully passed
|
| + // the UAC prompt.
|
| + return scoped_ptr<DaemonInstallerWin>(
|
| + new DaemonComInstallerWin(update3, done));
|
| + } else if (result == 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.
|
| + return scoped_ptr<DaemonInstallerWin>(
|
| + new DaemonCommandLineInstallerWin(done));
|
| + } else {
|
| + // The user declined the UAC prompt or some other error occured.
|
| + done.Run(result);
|
| + return scoped_ptr<DaemonInstallerWin>();
|
| + }
|
| +}
|
| +
|
| +} // namespace remoting
|
|
|