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 |