- add sources.
[platform/framework/web/crosswalk.git] / src / remoting / host / setup / me2me_native_messaging_host.cc
1 // Copyright 2013 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/me2me_native_messaging_host.h"
6
7 #include <string>
8
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringize_macros.h"
18 #include "base/threading/thread.h"
19 #include "base/values.h"
20 #include "google_apis/gaia/gaia_oauth_client.h"
21 #include "google_apis/google_api_keys.h"
22 #include "net/base/net_util.h"
23 #include "net/url_request/url_fetcher.h"
24 #include "remoting/base/rsa_key_pair.h"
25 #include "remoting/base/url_request_context.h"
26 #include "remoting/host/host_exit_codes.h"
27 #include "remoting/host/pairing_registry_delegate.h"
28 #include "remoting/host/pin_hash.h"
29 #include "remoting/host/setup/oauth_client.h"
30 #include "remoting/protocol/pairing_registry.h"
31
32 namespace {
33
34 const char kParentWindowSwitchName[] = "parent-window";
35
36 // redirect_uri to use when authenticating service accounts (service account
37 // codes are obtained "out-of-band", i.e., not through an OAuth redirect).
38 const char* kServiceAccountRedirectUri = "oob";
39
40 // Features supported in addition to the base protocol.
41 const char* kSupportedFeatures[] = {
42   "pairingRegistry",
43   "oauthClient"
44 };
45
46 // Helper to extract the "config" part of a message as a DictionaryValue.
47 // Returns NULL on failure, and logs an error message.
48 scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
49     const base::DictionaryValue& message) {
50   scoped_ptr<base::DictionaryValue> result;
51   const base::DictionaryValue* config_dict;
52   if (message.GetDictionary("config", &config_dict)) {
53     result.reset(config_dict->DeepCopy());
54   } else {
55     LOG(ERROR) << "'config' dictionary not found";
56   }
57   return result.Pass();
58 }
59
60 }  // namespace
61
62 namespace remoting {
63
64 NativeMessagingHost::NativeMessagingHost(
65     scoped_refptr<DaemonController> daemon_controller,
66     scoped_refptr<protocol::PairingRegistry> pairing_registry,
67     scoped_ptr<OAuthClient> oauth_client)
68     : daemon_controller_(daemon_controller),
69       pairing_registry_(pairing_registry),
70       oauth_client_(oauth_client.Pass()),
71       weak_factory_(this) {
72   weak_ptr_ = weak_factory_.GetWeakPtr();
73 }
74
75 NativeMessagingHost::~NativeMessagingHost() {
76 }
77
78 void NativeMessagingHost::ProcessMessage(
79     scoped_ptr<base::DictionaryValue> message,
80     const SendResponseCallback& done) {
81   scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
82
83   // If the client supplies an ID, it will expect it in the response. This
84   // might be a string or a number, so cope with both.
85   const base::Value* id;
86   if (message->Get("id", &id))
87     response->Set("id", id->DeepCopy());
88
89   std::string type;
90   if (!message->GetString("type", &type)) {
91     LOG(ERROR) << "'type' not found";
92     done.Run(scoped_ptr<base::DictionaryValue>());
93     return;
94   }
95
96   response->SetString("type", type + "Response");
97
98   bool success = false;
99   if (type == "hello") {
100     success = ProcessHello(*message, response.Pass(), done);
101   } else if (type == "clearPairedClients") {
102     success = ProcessClearPairedClients(*message, response.Pass(), done);
103   } else if (type == "deletePairedClient") {
104     success = ProcessDeletePairedClient(*message, response.Pass(), done);
105   } else if (type == "getHostName") {
106     success = ProcessGetHostName(*message, response.Pass(), done);
107   } else if (type == "getPinHash") {
108     success = ProcessGetPinHash(*message, response.Pass(), done);
109   } else if (type == "generateKeyPair") {
110     success = ProcessGenerateKeyPair(*message, response.Pass(), done);
111   } else if (type == "updateDaemonConfig") {
112     success = ProcessUpdateDaemonConfig(*message, response.Pass(), done);
113   } else if (type == "getDaemonConfig") {
114     success = ProcessGetDaemonConfig(*message, response.Pass(), done);
115   } else if (type == "getPairedClients") {
116     success = ProcessGetPairedClients(*message, response.Pass(), done);
117   } else if (type == "getUsageStatsConsent") {
118     success = ProcessGetUsageStatsConsent(*message, response.Pass(), done);
119   } else if (type == "startDaemon") {
120     success = ProcessStartDaemon(*message, response.Pass(), done);
121   } else if (type == "stopDaemon") {
122     success = ProcessStopDaemon(*message, response.Pass(), done);
123   } else if (type == "getDaemonState") {
124     success = ProcessGetDaemonState(*message, response.Pass(), done);
125   } else if (type == "getHostClientId") {
126     success = ProcessGetHostClientId(*message, response.Pass(), done);
127   } else if (type == "getCredentialsFromAuthCode") {
128     success = ProcessGetCredentialsFromAuthCode(*message, response.Pass(),
129                                                 done);
130   } else {
131     LOG(ERROR) << "Unsupported request type: " << type;
132   }
133
134   if (!success)
135     done.Run(scoped_ptr<base::DictionaryValue>());
136 }
137
138 bool NativeMessagingHost::ProcessHello(
139     const base::DictionaryValue& message,
140     scoped_ptr<base::DictionaryValue> response,
141     const SendResponseCallback& done) {
142   response->SetString("version", STRINGIZE(VERSION));
143   scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
144   supported_features_list->AppendStrings(std::vector<std::string>(
145       kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
146   response->Set("supportedFeatures", supported_features_list.release());
147   done.Run(response.Pass());
148   return true;
149 }
150
151 bool NativeMessagingHost::ProcessClearPairedClients(
152     const base::DictionaryValue& message,
153     scoped_ptr<base::DictionaryValue> response,
154     const SendResponseCallback& done) {
155   if (pairing_registry_) {
156     pairing_registry_->ClearAllPairings(
157         base::Bind(&NativeMessagingHost::SendBooleanResult, weak_ptr_,
158                    done, base::Passed(&response)));
159   } else {
160     SendBooleanResult(done, response.Pass(), false);
161   }
162   return true;
163 }
164
165 bool NativeMessagingHost::ProcessDeletePairedClient(
166     const base::DictionaryValue& message,
167     scoped_ptr<base::DictionaryValue> response,
168     const SendResponseCallback& done) {
169   std::string client_id;
170   if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) {
171     LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
172                << "' string not found.";
173     return false;
174   }
175
176   if (pairing_registry_) {
177     pairing_registry_->DeletePairing(
178         client_id, base::Bind(&NativeMessagingHost::SendBooleanResult,
179                               weak_ptr_, done, base::Passed(&response)));
180   } else {
181     SendBooleanResult(done, response.Pass(), false);
182   }
183   return true;
184 }
185
186 bool NativeMessagingHost::ProcessGetHostName(
187     const base::DictionaryValue& message,
188     scoped_ptr<base::DictionaryValue> response,
189     const SendResponseCallback& done) {
190   response->SetString("hostname", net::GetHostName());
191   done.Run(response.Pass());
192   return true;
193 }
194
195 bool NativeMessagingHost::ProcessGetPinHash(
196     const base::DictionaryValue& message,
197     scoped_ptr<base::DictionaryValue> response,
198     const SendResponseCallback& done) {
199   std::string host_id;
200   if (!message.GetString("hostId", &host_id)) {
201     LOG(ERROR) << "'hostId' not found: " << message;
202     return false;
203   }
204   std::string pin;
205   if (!message.GetString("pin", &pin)) {
206     LOG(ERROR) << "'pin' not found: " << message;
207     return false;
208   }
209   response->SetString("hash", MakeHostPinHash(host_id, pin));
210   done.Run(response.Pass());
211   return true;
212 }
213
214 bool NativeMessagingHost::ProcessGenerateKeyPair(
215     const base::DictionaryValue& message,
216     scoped_ptr<base::DictionaryValue> response,
217     const SendResponseCallback& done) {
218   scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
219   response->SetString("privateKey", key_pair->ToString());
220   response->SetString("publicKey", key_pair->GetPublicKey());
221   done.Run(response.Pass());
222   return true;
223 }
224
225 bool NativeMessagingHost::ProcessUpdateDaemonConfig(
226     const base::DictionaryValue& message,
227     scoped_ptr<base::DictionaryValue> response,
228     const SendResponseCallback& done) {
229   scoped_ptr<base::DictionaryValue> config_dict =
230       ConfigDictionaryFromMessage(message);
231   if (!config_dict)
232     return false;
233
234   daemon_controller_->UpdateConfig(
235       config_dict.Pass(),
236       base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_,
237                  done, base::Passed(&response)));
238   return true;
239 }
240
241 bool NativeMessagingHost::ProcessGetDaemonConfig(
242     const base::DictionaryValue& message,
243     scoped_ptr<base::DictionaryValue> response,
244     const SendResponseCallback& done) {
245   daemon_controller_->GetConfig(
246       base::Bind(&NativeMessagingHost::SendConfigResponse, weak_ptr_,
247                  done, base::Passed(&response)));
248   return true;
249 }
250
251 bool NativeMessagingHost::ProcessGetPairedClients(
252     const base::DictionaryValue& message,
253     scoped_ptr<base::DictionaryValue> response,
254     const SendResponseCallback& done) {
255   if (pairing_registry_) {
256     pairing_registry_->GetAllPairings(
257         base::Bind(&NativeMessagingHost::SendPairedClientsResponse, weak_ptr_,
258                    done, base::Passed(&response)));
259   } else {
260     scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
261     SendPairedClientsResponse(done, response.Pass(), no_paired_clients.Pass());
262   }
263   return true;
264 }
265
266 bool NativeMessagingHost::ProcessGetUsageStatsConsent(
267     const base::DictionaryValue& message,
268     scoped_ptr<base::DictionaryValue> response,
269     const SendResponseCallback& done) {
270   daemon_controller_->GetUsageStatsConsent(
271       base::Bind(&NativeMessagingHost::SendUsageStatsConsentResponse,
272                  weak_ptr_, done, base::Passed(&response)));
273   return true;
274 }
275
276 bool NativeMessagingHost::ProcessStartDaemon(
277     const base::DictionaryValue& message,
278     scoped_ptr<base::DictionaryValue> response,
279     const SendResponseCallback& done) {
280   bool consent;
281   if (!message.GetBoolean("consent", &consent)) {
282     LOG(ERROR) << "'consent' not found.";
283     return false;
284   }
285
286   scoped_ptr<base::DictionaryValue> config_dict =
287       ConfigDictionaryFromMessage(message);
288   if (!config_dict)
289     return false;
290
291   daemon_controller_->SetConfigAndStart(
292       config_dict.Pass(), consent,
293       base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_,
294                  done, base::Passed(&response)));
295   return true;
296 }
297
298 bool NativeMessagingHost::ProcessStopDaemon(
299     const base::DictionaryValue& message,
300     scoped_ptr<base::DictionaryValue> response,
301     const SendResponseCallback& done) {
302   daemon_controller_->Stop(
303       base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_,
304                  done, base::Passed(&response)));
305   return true;
306 }
307
308 bool NativeMessagingHost::ProcessGetDaemonState(
309     const base::DictionaryValue& message,
310     scoped_ptr<base::DictionaryValue> response,
311     const SendResponseCallback& done) {
312   DaemonController::State state = daemon_controller_->GetState();
313   switch (state) {
314     case DaemonController::STATE_NOT_IMPLEMENTED:
315       response->SetString("state", "NOT_IMPLEMENTED");
316       break;
317     case DaemonController::STATE_NOT_INSTALLED:
318       response->SetString("state", "NOT_INSTALLED");
319       break;
320     case DaemonController::STATE_INSTALLING:
321       response->SetString("state", "INSTALLING");
322       break;
323     case DaemonController::STATE_STOPPED:
324       response->SetString("state", "STOPPED");
325       break;
326     case DaemonController::STATE_STARTING:
327       response->SetString("state", "STARTING");
328       break;
329     case DaemonController::STATE_STARTED:
330       response->SetString("state", "STARTED");
331       break;
332     case DaemonController::STATE_STOPPING:
333       response->SetString("state", "STOPPING");
334       break;
335     case DaemonController::STATE_UNKNOWN:
336       response->SetString("state", "UNKNOWN");
337       break;
338   }
339   done.Run(response.Pass());
340   return true;
341 }
342
343 bool NativeMessagingHost::ProcessGetHostClientId(
344     const base::DictionaryValue& message,
345     scoped_ptr<base::DictionaryValue> response,
346     const SendResponseCallback& done) {
347   response->SetString("clientId", google_apis::GetOAuth2ClientID(
348       google_apis::CLIENT_REMOTING_HOST));
349   done.Run(response.Pass());
350   return true;
351 }
352
353 bool NativeMessagingHost::ProcessGetCredentialsFromAuthCode(
354     const base::DictionaryValue& message,
355     scoped_ptr<base::DictionaryValue> response,
356     const SendResponseCallback& done) {
357   std::string auth_code;
358   if (!message.GetString("authorizationCode", &auth_code)) {
359     LOG(ERROR) << "'authorizationCode' string not found.";
360     return false;
361   }
362
363   gaia::OAuthClientInfo oauth_client_info = {
364     google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
365     google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
366     kServiceAccountRedirectUri
367   };
368
369   oauth_client_->GetCredentialsFromAuthCode(
370       oauth_client_info, auth_code, base::Bind(
371           &NativeMessagingHost::SendCredentialsResponse, weak_ptr_,
372           done, base::Passed(&response)));
373
374   return true;
375 }
376
377 void NativeMessagingHost::SendConfigResponse(
378     const SendResponseCallback& done,
379     scoped_ptr<base::DictionaryValue> response,
380     scoped_ptr<base::DictionaryValue> config) {
381   if (config) {
382     response->Set("config", config.release());
383   } else {
384     response->Set("config", Value::CreateNullValue());
385   }
386   done.Run(response.Pass());
387 }
388
389 void NativeMessagingHost::SendPairedClientsResponse(
390     const SendResponseCallback& done,
391     scoped_ptr<base::DictionaryValue> response,
392     scoped_ptr<base::ListValue> pairings) {
393   response->Set("pairedClients", pairings.release());
394   done.Run(response.Pass());
395 }
396
397 void NativeMessagingHost::SendUsageStatsConsentResponse(
398     const SendResponseCallback& done,
399     scoped_ptr<base::DictionaryValue> response,
400     const DaemonController::UsageStatsConsent& consent) {
401   response->SetBoolean("supported", consent.supported);
402   response->SetBoolean("allowed", consent.allowed);
403   response->SetBoolean("setByPolicy", consent.set_by_policy);
404   done.Run(response.Pass());
405 }
406
407 void NativeMessagingHost::SendAsyncResult(
408     const SendResponseCallback& done,
409     scoped_ptr<base::DictionaryValue> response,
410     DaemonController::AsyncResult result) {
411   switch (result) {
412     case DaemonController::RESULT_OK:
413       response->SetString("result", "OK");
414       break;
415     case DaemonController::RESULT_FAILED:
416       response->SetString("result", "FAILED");
417       break;
418     case DaemonController::RESULT_CANCELLED:
419       response->SetString("result", "CANCELLED");
420       break;
421     case DaemonController::RESULT_FAILED_DIRECTORY:
422       response->SetString("result", "FAILED_DIRECTORY");
423       break;
424   }
425   done.Run(response.Pass());
426 }
427
428 void NativeMessagingHost::SendBooleanResult(
429     const SendResponseCallback& done,
430     scoped_ptr<base::DictionaryValue> response, bool result) {
431   response->SetBoolean("result", result);
432   done.Run(response.Pass());
433 }
434
435 void NativeMessagingHost::SendCredentialsResponse(
436     const SendResponseCallback& done,
437     scoped_ptr<base::DictionaryValue> response,
438     const std::string& user_email,
439     const std::string& refresh_token) {
440   response->SetString("userEmail", user_email);
441   response->SetString("refreshToken", refresh_token);
442   done.Run(response.Pass());
443 }
444
445 int NativeMessagingHostMain() {
446 #if defined(OS_WIN)
447   // GetStdHandle() returns pseudo-handles for stdin and stdout even if
448   // the hosting executable specifies "Windows" subsystem. However the returned
449   // handles are invalid in that case unless standard input and output are
450   // redirected to a pipe or file.
451   base::PlatformFile read_file = GetStdHandle(STD_INPUT_HANDLE);
452   base::PlatformFile write_file = GetStdHandle(STD_OUTPUT_HANDLE);
453 #elif defined(OS_POSIX)
454   base::PlatformFile read_file = STDIN_FILENO;
455   base::PlatformFile write_file = STDOUT_FILENO;
456 #else
457 #error Not implemented.
458 #endif
459
460   // Mac OS X requires that the main thread be a UI message loop in order to
461   // receive distributed notifications from the System Preferences pane. An
462   // IO thread is needed for the pairing registry and URL context getter.
463   base::Thread io_thread("io_thread");
464   io_thread.StartWithOptions(
465       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
466
467   base::MessageLoopForUI message_loop;
468   base::RunLoop run_loop;
469
470   scoped_refptr<DaemonController> daemon_controller =
471       DaemonController::Create();
472
473   // Pass handle of the native view to the controller so that the UAC prompts
474   // are focused properly.
475   const CommandLine* command_line = CommandLine::ForCurrentProcess();
476   if (command_line->HasSwitch(kParentWindowSwitchName)) {
477     std::string native_view =
478         command_line->GetSwitchValueASCII(kParentWindowSwitchName);
479     int64 native_view_handle = 0;
480     if (base::StringToInt64(native_view, &native_view_handle)) {
481       daemon_controller->SetWindow(reinterpret_cast<void*>(native_view_handle));
482     } else {
483       LOG(WARNING) << "Invalid parameter value --" << kParentWindowSwitchName
484                    << "=" << native_view;
485     }
486   }
487
488   // OAuth client (for credential requests).
489   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter(
490       new URLRequestContextGetter(io_thread.message_loop_proxy()));
491   scoped_ptr<OAuthClient> oauth_client(
492       new OAuthClient(url_request_context_getter));
493
494   net::URLFetcher::SetIgnoreCertificateRequests(true);
495
496   // Create the pairing registry and native messaging host.
497   scoped_refptr<protocol::PairingRegistry> pairing_registry =
498       CreatePairingRegistry(io_thread.message_loop_proxy());
499   scoped_ptr<NativeMessagingChannel::Delegate> host(
500       new NativeMessagingHost(daemon_controller,
501                               pairing_registry,
502                               oauth_client.Pass()));
503
504   // Set up the native messaging channel.
505   scoped_ptr<NativeMessagingChannel> channel(
506       new NativeMessagingChannel(host.Pass(), read_file, write_file));
507   channel->Start(run_loop.QuitClosure());
508
509   // Run the loop until channel is alive.
510   run_loop.Run();
511   return kSuccessExitCode;
512 }
513
514 }  // namespace remoting