Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / remoting / host / setup / daemon_installer_win.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "remoting/host/setup/daemon_installer_win.h"
6
7 #include <windows.h>
8
9 #include "base/bind.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/process/launch.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "base/timer/timer.h"
17 #include "base/win/object_watcher.h"
18 #include "base/win/registry.h"
19 #include "base/win/scoped_bstr.h"
20 #include "base/win/scoped_comptr.h"
21 #include "base/win/scoped_handle.h"
22 #include "base/win/scoped_variant.h"
23 #include "base/win/windows_version.h"
24 #include "google_update/google_update_idl.h"
25 #include "remoting/base/dispatch_win.h"
26 #include "remoting/host/win/omaha.h"
27
28 using base::win::ScopedBstr;
29 using base::win::ScopedComPtr;
30 using base::win::ScopedVariant;
31
32 namespace {
33
34 // ProgID of the per-machine Omaha COM server.
35 const wchar_t kGoogleUpdate[] = L"GoogleUpdate.Update3WebMachine";
36
37 // The COM elevation moniker for the per-machine Omaha COM server.
38 const wchar_t kGoogleUpdateElevationMoniker[] =
39     L"Elevation:Administrator!new:GoogleUpdate.Update3WebMachine";
40
41 // The registry key where the configuration of Omaha is stored.
42 const wchar_t kOmahaUpdateKeyName[] = L"Software\\Google\\Update";
43
44 // The name of the value where the full path to GoogleUpdate.exe is stored.
45 const wchar_t kOmahaPathValueName[] = L"path";
46
47 // The command line format string for GoogleUpdate.exe
48 const wchar_t kGoogleUpdateCommandLineFormat[] =
49     L"\"%ls\" /install \"bundlename=Chromoting%%20Host&appguid=%ls&"
50     L"appname=Chromoting%%20Host&needsadmin=True&lang=%ls\"";
51
52 // TODO(alexeypa): Get the desired laungage from the web app.
53 const wchar_t kOmahaLanguage[] = L"en";
54
55 // An empty string for optional parameters.
56 const wchar_t kOmahaEmpty[] = L"";
57
58 // The installation status polling interval.
59 const int kOmahaPollIntervalMs = 500;
60
61 }  // namespace
62
63 namespace remoting {
64
65 // This class implements on-demand installation of the Chromoting Host via
66 // per-machine Omaha instance.
67 class DaemonComInstallerWin : public DaemonInstallerWin {
68  public:
69   DaemonComInstallerWin(const ScopedComPtr<IDispatch>& update3,
70                         const CompletionCallback& done);
71
72   // DaemonInstallerWin implementation.
73   virtual void Install() override;
74
75  private:
76   // Polls the installation status performing state-specific actions (such as
77   // starting installation once download has finished).
78   void PollInstallationStatus();
79
80   // Omaha interfaces.
81   ScopedVariant app_;
82   ScopedVariant bundle_;
83   ScopedComPtr<IDispatch> update3_;
84
85   base::Timer polling_timer_;
86 };
87
88 // This class implements on-demand installation of the Chromoting Host by
89 // launching a per-user instance of Omaha and requesting elevation.
90 class DaemonCommandLineInstallerWin
91     : public DaemonInstallerWin,
92       public base::win::ObjectWatcher::Delegate {
93  public:
94   DaemonCommandLineInstallerWin(const CompletionCallback& done);
95   ~DaemonCommandLineInstallerWin();
96
97   // DaemonInstallerWin implementation.
98   virtual void Install() override;
99
100   // base::win::ObjectWatcher::Delegate implementation.
101   virtual void OnObjectSignaled(HANDLE object) override;
102
103  private:
104   // Handle of the launched process.
105   base::win::ScopedHandle process_;
106
107   // Used to determine when the launched process terminates.
108   base::win::ObjectWatcher process_watcher_;
109 };
110
111 DaemonComInstallerWin::DaemonComInstallerWin(
112     const ScopedComPtr<IDispatch>& update3,
113     const CompletionCallback& done)
114     : DaemonInstallerWin(done),
115       update3_(update3),
116       polling_timer_(
117           FROM_HERE,
118           base::TimeDelta::FromMilliseconds(kOmahaPollIntervalMs),
119           base::Bind(&DaemonComInstallerWin::PollInstallationStatus,
120                      base::Unretained(this)),
121           false) {
122 }
123
124 void DaemonComInstallerWin::Install() {
125   // Create an app bundle.
126   HRESULT hr = dispatch::Invoke(update3_.get(), L"createAppBundleWeb",
127                                 DISPATCH_METHOD, bundle_.Receive());
128   if (FAILED(hr)) {
129     Done(hr);
130     return;
131   }
132   if (bundle_.type() != VT_DISPATCH) {
133     Done(DISP_E_TYPEMISMATCH);
134     return;
135   }
136
137   hr = dispatch::Invoke(V_DISPATCH(&bundle_), L"initialize", DISPATCH_METHOD,
138                         NULL);
139   if (FAILED(hr)) {
140     Done(hr);
141     return;
142   }
143
144   // Add Chromoting Host to the bundle.
145   ScopedVariant appid(kHostOmahaAppid);
146   ScopedVariant empty(kOmahaEmpty);
147   ScopedVariant language(kOmahaLanguage);
148   hr = dispatch::Invoke(V_DISPATCH(&bundle_), L"createApp", DISPATCH_METHOD,
149                         appid, empty, language, empty, NULL);
150   if (FAILED(hr)) {
151     Done(hr);
152     return;
153   }
154
155   hr = dispatch::Invoke(V_DISPATCH(&bundle_), L"checkForUpdate",
156                         DISPATCH_METHOD, NULL);
157   if (FAILED(hr)) {
158     Done(hr);
159     return;
160   }
161
162   hr = dispatch::Invoke(V_DISPATCH(&bundle_), L"appWeb",
163                         DISPATCH_PROPERTYGET, ScopedVariant(0), app_.Receive());
164   if (FAILED(hr)) {
165     Done(hr);
166     return;
167   }
168   if (app_.type() != VT_DISPATCH) {
169     Done(DISP_E_TYPEMISMATCH);
170     return;
171   }
172
173   // Now poll for the installation status.
174   PollInstallationStatus();
175 }
176
177 void DaemonComInstallerWin::PollInstallationStatus() {
178   // Get the current application installation state.
179   // N.B. The object underlying the ICurrentState interface has static data that
180   // does not get updated as the server state changes. To get the most "current"
181   // state, the currentState property needs to be queried again.
182   ScopedVariant current_state;
183   HRESULT hr = dispatch::Invoke(V_DISPATCH(&app_), L"currentState",
184                                 DISPATCH_PROPERTYGET, current_state.Receive());
185   if (FAILED(hr)) {
186     Done(hr);
187     return;
188   }
189   if (current_state.type() != VT_DISPATCH) {
190     Done(DISP_E_TYPEMISMATCH);
191     return;
192   }
193
194   ScopedVariant state;
195   hr = dispatch::Invoke(V_DISPATCH(&current_state), L"stateValue",
196                         DISPATCH_PROPERTYGET, state.Receive());
197   if (state.type() != VT_I4) {
198     Done(DISP_E_TYPEMISMATCH);
199     return;
200   }
201
202   // Perform state-specific actions.
203   switch (V_I4(&state)) {
204     case STATE_INIT:
205     case STATE_WAITING_TO_CHECK_FOR_UPDATE:
206     case STATE_CHECKING_FOR_UPDATE:
207     case STATE_WAITING_TO_DOWNLOAD:
208     case STATE_RETRYING_DOWNLOAD:
209     case STATE_DOWNLOADING:
210     case STATE_WAITING_TO_INSTALL:
211     case STATE_INSTALLING:
212     case STATE_PAUSED:
213       break;
214
215     case STATE_UPDATE_AVAILABLE:
216       hr = dispatch::Invoke(V_DISPATCH(&bundle_), L"download",
217                             DISPATCH_METHOD, NULL);
218       if (FAILED(hr)) {
219         Done(hr);
220         return;
221       }
222       break;
223
224     case STATE_DOWNLOAD_COMPLETE:
225     case STATE_EXTRACTING:
226     case STATE_APPLYING_DIFFERENTIAL_PATCH:
227     case STATE_READY_TO_INSTALL:
228       hr = dispatch::Invoke(V_DISPATCH(&bundle_), L"install",
229                             DISPATCH_METHOD, NULL);
230       if (FAILED(hr)) {
231         Done(hr);
232         return;
233       }
234       break;
235
236     case STATE_INSTALL_COMPLETE:
237     case STATE_NO_UPDATE:
238       // Installation complete or not required. Report success.
239       Done(S_OK);
240       return;
241
242     case STATE_ERROR: {
243       ScopedVariant error_code;
244       hr = dispatch::Invoke(V_DISPATCH(&current_state), L"errorCode",
245                             DISPATCH_PROPERTYGET, error_code.Receive());
246       if (FAILED(hr)) {
247         Done(hr);
248         return;
249       }
250       if (error_code.type() != VT_UI4) {
251         Done(DISP_E_TYPEMISMATCH);
252         return;
253       }
254       Done(V_UI4(&error_code));
255       return;
256     }
257
258     default:
259       LOG(ERROR) << "Unknown bundle state: " << V_I4(&state) << ".";
260       Done(E_FAIL);
261       return;
262   }
263
264   // Keep polling.
265   polling_timer_.Reset();
266 }
267
268 DaemonCommandLineInstallerWin::DaemonCommandLineInstallerWin(
269     const CompletionCallback& done) : DaemonInstallerWin(done) {
270 }
271
272 DaemonCommandLineInstallerWin::~DaemonCommandLineInstallerWin() {
273   process_watcher_.StopWatching();
274 }
275
276 void DaemonCommandLineInstallerWin::Install() {
277   // Get the full path to GoogleUpdate.exe from the registry.
278   base::win::RegKey update_key;
279   LONG result = update_key.Open(HKEY_CURRENT_USER,
280                                 kOmahaUpdateKeyName,
281                                 KEY_READ);
282   if (result != ERROR_SUCCESS) {
283     Done(HRESULT_FROM_WIN32(result));
284     return;
285   }
286
287   // presubmit: allow wstring
288   std::wstring google_update;
289   result = update_key.ReadValue(kOmahaPathValueName, &google_update);
290   if (result != ERROR_SUCCESS) {
291     Done(HRESULT_FROM_WIN32(result));
292     return;
293   }
294
295   // Launch the updater process and wait for its termination.
296   base::string16 command_line = base::WideToUTF16(
297       base::StringPrintf(kGoogleUpdateCommandLineFormat,
298                          google_update.c_str(),
299                          kHostOmahaAppid,
300                          kOmahaLanguage));
301
302   base::LaunchOptions options;
303   if (!base::LaunchProcess(command_line, options, &process_)) {
304     result = GetLastError();
305     Done(HRESULT_FROM_WIN32(result));
306     return;
307   }
308
309   if (!process_watcher_.StartWatching(process_.Get(), this)) {
310     result = GetLastError();
311     Done(HRESULT_FROM_WIN32(result));
312     return;
313   }
314 }
315
316 void DaemonCommandLineInstallerWin::OnObjectSignaled(HANDLE object) {
317   // Check if the updater process returned success.
318   DWORD exit_code;
319   if (GetExitCodeProcess(process_.Get(), &exit_code) && exit_code == 0) {
320     Done(S_OK);
321   } else {
322     Done(E_FAIL);
323   }
324 }
325
326 DaemonInstallerWin::DaemonInstallerWin(const CompletionCallback& done)
327     : done_(done) {
328 }
329
330 DaemonInstallerWin::~DaemonInstallerWin() {
331 }
332
333 void DaemonInstallerWin::Done(HRESULT result) {
334   CompletionCallback done = done_;
335   done_.Reset();
336   done.Run(result);
337 }
338
339 // static
340 scoped_ptr<DaemonInstallerWin> DaemonInstallerWin::Create(
341     HWND window_handle,
342     CompletionCallback done) {
343   HRESULT result = E_FAIL;
344   ScopedComPtr<IDispatch> update3;
345
346   // Check if the machine instance of Omaha is available. The COM elevation is
347   // supported on Vista+, so on XP/W2K3 we assume that we are running under
348   // a privileged user and get ACCESS_DENIED later if we are not.
349   if (base::win::GetVersion() < base::win::VERSION_VISTA) {
350     CLSID class_id;
351     result = CLSIDFromProgID(kGoogleUpdate, &class_id);
352     if (SUCCEEDED(result)) {
353       result = CoCreateInstance(class_id,
354                                 NULL,
355                                 CLSCTX_LOCAL_SERVER,
356                                 IID_IDispatch,
357                                 update3.ReceiveVoid());
358     }
359   } else {
360     BIND_OPTS3 bind_options;
361     memset(&bind_options, 0, sizeof(bind_options));
362     bind_options.cbStruct = sizeof(bind_options);
363     bind_options.hwnd = GetTopLevelWindow(window_handle);
364     bind_options.dwClassContext = CLSCTX_LOCAL_SERVER;
365     result = CoGetObject(kGoogleUpdateElevationMoniker,
366                          &bind_options,
367                          IID_IDispatch,
368                          update3.ReceiveVoid());
369   }
370
371   if (result == CO_E_CLASSSTRING) {
372     // The machine instance of Omaha is not available so we will have to run
373     // GoogleUpdate.exe manually passing "needsadmin=True". This will cause
374     // Omaha to install the machine instance first and then install Chromoting
375     // Host.
376     return make_scoped_ptr(new DaemonCommandLineInstallerWin(done));
377   }
378
379   if (!SUCCEEDED(result)) {
380     // The user declined the UAC prompt or some other error occured.
381     done.Run(result);
382     return nullptr;
383   }
384
385   // The machine instance of Omaha is available and we successfully passed
386   // the UAC prompt.
387   return make_scoped_ptr(new DaemonComInstallerWin(update3, done));
388 }
389
390 HWND GetTopLevelWindow(HWND window) {
391   if (window == NULL) {
392     return NULL;
393   }
394
395   for (;;) {
396     LONG style = GetWindowLong(window, GWL_STYLE);
397     if ((style & WS_OVERLAPPEDWINDOW) == WS_OVERLAPPEDWINDOW ||
398         (style & WS_POPUP) == WS_POPUP) {
399       return window;
400     }
401
402     HWND parent = GetAncestor(window, GA_PARENT);
403     if (parent == NULL) {
404       return window;
405     }
406
407     window = parent;
408   }
409 }
410
411 }  // namespace remoting