fabc8b0fcb1ba3ab6975e18257ffe4b8c27a5634
[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/callback_helpers.h"
13 #include "base/logging.h"
14 #include "base/strings/stringize_macros.h"
15 #include "base/threading/thread.h"
16 #include "base/values.h"
17 #include "google_apis/gaia/gaia_oauth_client.h"
18 #include "google_apis/google_api_keys.h"
19 #include "net/base/net_util.h"
20 #include "remoting/base/rsa_key_pair.h"
21 #include "remoting/host/pin_hash.h"
22 #include "remoting/host/setup/oauth_client.h"
23 #include "remoting/protocol/pairing_registry.h"
24
25 namespace {
26
27 // redirect_uri to use when authenticating service accounts (service account
28 // codes are obtained "out-of-band", i.e., not through an OAuth redirect).
29 const char* kServiceAccountRedirectUri = "oob";
30
31 // Features supported in addition to the base protocol.
32 const char* kSupportedFeatures[] = {
33   "pairingRegistry",
34   "oauthClient"
35 };
36
37 // Helper to extract the "config" part of a message as a DictionaryValue.
38 // Returns NULL on failure, and logs an error message.
39 scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
40     const base::DictionaryValue& message) {
41   scoped_ptr<base::DictionaryValue> result;
42   const base::DictionaryValue* config_dict;
43   if (message.GetDictionary("config", &config_dict)) {
44     result.reset(config_dict->DeepCopy());
45   } else {
46     LOG(ERROR) << "'config' dictionary not found";
47   }
48   return result.Pass();
49 }
50
51 }  // namespace
52
53 namespace remoting {
54
55 Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
56     scoped_ptr<NativeMessagingChannel> channel,
57     scoped_refptr<DaemonController> daemon_controller,
58     scoped_refptr<protocol::PairingRegistry> pairing_registry,
59     scoped_ptr<OAuthClient> oauth_client)
60     : channel_(channel.Pass()),
61       daemon_controller_(daemon_controller),
62       pairing_registry_(pairing_registry),
63       oauth_client_(oauth_client.Pass()),
64       weak_factory_(this) {
65   weak_ptr_ = weak_factory_.GetWeakPtr();
66 }
67
68 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
69   DCHECK(thread_checker_.CalledOnValidThread());
70 }
71
72 void Me2MeNativeMessagingHost::Start(
73       const base::Closure& quit_closure) {
74   DCHECK(thread_checker_.CalledOnValidThread());
75
76   channel_->Start(
77       base::Bind(&Me2MeNativeMessagingHost::ProcessMessage, weak_ptr_),
78       quit_closure);
79 }
80
81 void Me2MeNativeMessagingHost::ProcessMessage(
82     scoped_ptr<base::DictionaryValue> message) {
83   DCHECK(thread_checker_.CalledOnValidThread());
84
85   scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
86
87   // If the client supplies an ID, it will expect it in the response. This
88   // might be a string or a number, so cope with both.
89   const base::Value* id;
90   if (message->Get("id", &id))
91     response->Set("id", id->DeepCopy());
92
93   std::string type;
94   if (!message->GetString("type", &type)) {
95     LOG(ERROR) << "'type' not found";
96     channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
97     return;
98   }
99
100   response->SetString("type", type + "Response");
101
102   if (type == "hello") {
103     ProcessHello(*message, response.Pass());
104   } else if (type == "clearPairedClients") {
105     ProcessClearPairedClients(*message, response.Pass());
106   } else if (type == "deletePairedClient") {
107     ProcessDeletePairedClient(*message, response.Pass());
108   } else if (type == "getHostName") {
109     ProcessGetHostName(*message, response.Pass());
110   } else if (type == "getPinHash") {
111     ProcessGetPinHash(*message, response.Pass());
112   } else if (type == "generateKeyPair") {
113     ProcessGenerateKeyPair(*message, response.Pass());
114   } else if (type == "updateDaemonConfig") {
115     ProcessUpdateDaemonConfig(*message, response.Pass());
116   } else if (type == "getDaemonConfig") {
117     ProcessGetDaemonConfig(*message, response.Pass());
118   } else if (type == "getPairedClients") {
119     ProcessGetPairedClients(*message, response.Pass());
120   } else if (type == "getUsageStatsConsent") {
121     ProcessGetUsageStatsConsent(*message, response.Pass());
122   } else if (type == "startDaemon") {
123     ProcessStartDaemon(*message, response.Pass());
124   } else if (type == "stopDaemon") {
125     ProcessStopDaemon(*message, response.Pass());
126   } else if (type == "getDaemonState") {
127     ProcessGetDaemonState(*message, response.Pass());
128   } else if (type == "getHostClientId") {
129     ProcessGetHostClientId(*message, response.Pass());
130   } else if (type == "getCredentialsFromAuthCode") {
131     ProcessGetCredentialsFromAuthCode(*message, response.Pass());
132   } else {
133     LOG(ERROR) << "Unsupported request type: " << type;
134     OnError();
135   }
136 }
137
138 void Me2MeNativeMessagingHost::ProcessHello(
139     const base::DictionaryValue& message,
140     scoped_ptr<base::DictionaryValue> response) {
141   DCHECK(thread_checker_.CalledOnValidThread());
142
143   response->SetString("version", STRINGIZE(VERSION));
144   scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
145   supported_features_list->AppendStrings(std::vector<std::string>(
146       kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
147   response->Set("supportedFeatures", supported_features_list.release());
148   channel_->SendMessage(response.Pass());
149 }
150
151 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
152     const base::DictionaryValue& message,
153     scoped_ptr<base::DictionaryValue> response) {
154   DCHECK(thread_checker_.CalledOnValidThread());
155
156   if (pairing_registry_) {
157     pairing_registry_->ClearAllPairings(
158         base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
159                    base::Passed(&response)));
160   } else {
161     SendBooleanResult(response.Pass(), false);
162   }
163 }
164
165 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
166     const base::DictionaryValue& message,
167     scoped_ptr<base::DictionaryValue> response) {
168   DCHECK(thread_checker_.CalledOnValidThread());
169
170   std::string client_id;
171   if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) {
172     LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
173                << "' string not found.";
174     OnError();
175     return;
176   }
177
178   if (pairing_registry_) {
179     pairing_registry_->DeletePairing(
180         client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
181                               weak_ptr_, base::Passed(&response)));
182   } else {
183     SendBooleanResult(response.Pass(), false);
184   }
185 }
186
187 void Me2MeNativeMessagingHost::ProcessGetHostName(
188     const base::DictionaryValue& message,
189     scoped_ptr<base::DictionaryValue> response) {
190   DCHECK(thread_checker_.CalledOnValidThread());
191
192   response->SetString("hostname", net::GetHostName());
193   channel_->SendMessage(response.Pass());
194 }
195
196 void Me2MeNativeMessagingHost::ProcessGetPinHash(
197     const base::DictionaryValue& message,
198     scoped_ptr<base::DictionaryValue> response) {
199   DCHECK(thread_checker_.CalledOnValidThread());
200
201   std::string host_id;
202   if (!message.GetString("hostId", &host_id)) {
203     LOG(ERROR) << "'hostId' not found: " << message;
204     OnError();
205     return;
206   }
207   std::string pin;
208   if (!message.GetString("pin", &pin)) {
209     LOG(ERROR) << "'pin' not found: " << message;
210     OnError();
211     return;
212   }
213   response->SetString("hash", MakeHostPinHash(host_id, pin));
214   channel_->SendMessage(response.Pass());
215 }
216
217 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
218     const base::DictionaryValue& message,
219     scoped_ptr<base::DictionaryValue> response) {
220   DCHECK(thread_checker_.CalledOnValidThread());
221
222   scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
223   response->SetString("privateKey", key_pair->ToString());
224   response->SetString("publicKey", key_pair->GetPublicKey());
225   channel_->SendMessage(response.Pass());
226 }
227
228 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
229     const base::DictionaryValue& message,
230     scoped_ptr<base::DictionaryValue> response) {
231   DCHECK(thread_checker_.CalledOnValidThread());
232
233   scoped_ptr<base::DictionaryValue> config_dict =
234       ConfigDictionaryFromMessage(message);
235   if (!config_dict) {
236     OnError();
237     return;
238   }
239
240   daemon_controller_->UpdateConfig(
241       config_dict.Pass(),
242       base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
243                  base::Passed(&response)));
244 }
245
246 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
247     const base::DictionaryValue& message,
248     scoped_ptr<base::DictionaryValue> response) {
249   DCHECK(thread_checker_.CalledOnValidThread());
250
251   daemon_controller_->GetConfig(
252       base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
253                  base::Passed(&response)));
254 }
255
256 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
257     const base::DictionaryValue& message,
258     scoped_ptr<base::DictionaryValue> response) {
259   DCHECK(thread_checker_.CalledOnValidThread());
260
261   if (pairing_registry_) {
262     pairing_registry_->GetAllPairings(
263         base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
264                    weak_ptr_, base::Passed(&response)));
265   } else {
266     scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
267     SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
268   }
269 }
270
271 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
272     const base::DictionaryValue& message,
273     scoped_ptr<base::DictionaryValue> response) {
274   DCHECK(thread_checker_.CalledOnValidThread());
275
276   daemon_controller_->GetUsageStatsConsent(
277       base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
278                  weak_ptr_, base::Passed(&response)));
279 }
280
281 void Me2MeNativeMessagingHost::ProcessStartDaemon(
282     const base::DictionaryValue& message,
283     scoped_ptr<base::DictionaryValue> response) {
284   DCHECK(thread_checker_.CalledOnValidThread());
285
286   bool consent;
287   if (!message.GetBoolean("consent", &consent)) {
288     LOG(ERROR) << "'consent' not found.";
289     OnError();
290     return;
291   }
292
293   scoped_ptr<base::DictionaryValue> config_dict =
294       ConfigDictionaryFromMessage(message);
295   if (!config_dict) {
296     OnError();
297     return;
298   }
299
300   daemon_controller_->SetConfigAndStart(
301       config_dict.Pass(), consent,
302       base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
303                  base::Passed(&response)));
304 }
305
306 void Me2MeNativeMessagingHost::ProcessStopDaemon(
307     const base::DictionaryValue& message,
308     scoped_ptr<base::DictionaryValue> response) {
309   DCHECK(thread_checker_.CalledOnValidThread());
310
311   daemon_controller_->Stop(
312       base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
313                  base::Passed(&response)));
314 }
315
316 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
317     const base::DictionaryValue& message,
318     scoped_ptr<base::DictionaryValue> response) {
319   DCHECK(thread_checker_.CalledOnValidThread());
320
321   DaemonController::State state = daemon_controller_->GetState();
322   switch (state) {
323     case DaemonController::STATE_NOT_IMPLEMENTED:
324       response->SetString("state", "NOT_IMPLEMENTED");
325       break;
326     case DaemonController::STATE_NOT_INSTALLED:
327       response->SetString("state", "NOT_INSTALLED");
328       break;
329     case DaemonController::STATE_INSTALLING:
330       response->SetString("state", "INSTALLING");
331       break;
332     case DaemonController::STATE_STOPPED:
333       response->SetString("state", "STOPPED");
334       break;
335     case DaemonController::STATE_STARTING:
336       response->SetString("state", "STARTING");
337       break;
338     case DaemonController::STATE_STARTED:
339       response->SetString("state", "STARTED");
340       break;
341     case DaemonController::STATE_STOPPING:
342       response->SetString("state", "STOPPING");
343       break;
344     case DaemonController::STATE_UNKNOWN:
345       response->SetString("state", "UNKNOWN");
346       break;
347   }
348   channel_->SendMessage(response.Pass());
349 }
350
351 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
352     const base::DictionaryValue& message,
353     scoped_ptr<base::DictionaryValue> response) {
354   DCHECK(thread_checker_.CalledOnValidThread());
355
356   response->SetString("clientId", google_apis::GetOAuth2ClientID(
357       google_apis::CLIENT_REMOTING_HOST));
358   channel_->SendMessage(response.Pass());
359 }
360
361 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
362     const base::DictionaryValue& message,
363     scoped_ptr<base::DictionaryValue> response) {
364   DCHECK(thread_checker_.CalledOnValidThread());
365
366   std::string auth_code;
367   if (!message.GetString("authorizationCode", &auth_code)) {
368     LOG(ERROR) << "'authorizationCode' string not found.";
369     OnError();
370     return;
371   }
372
373   gaia::OAuthClientInfo oauth_client_info = {
374     google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
375     google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
376     kServiceAccountRedirectUri
377   };
378
379   oauth_client_->GetCredentialsFromAuthCode(
380       oauth_client_info, auth_code, base::Bind(
381           &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
382           base::Passed(&response)));
383 }
384
385 void Me2MeNativeMessagingHost::SendConfigResponse(
386     scoped_ptr<base::DictionaryValue> response,
387     scoped_ptr<base::DictionaryValue> config) {
388   DCHECK(thread_checker_.CalledOnValidThread());
389
390   if (config) {
391     response->Set("config", config.release());
392   } else {
393     response->Set("config", base::Value::CreateNullValue());
394   }
395   channel_->SendMessage(response.Pass());
396 }
397
398 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
399     scoped_ptr<base::DictionaryValue> response,
400     scoped_ptr<base::ListValue> pairings) {
401   DCHECK(thread_checker_.CalledOnValidThread());
402
403   response->Set("pairedClients", pairings.release());
404   channel_->SendMessage(response.Pass());
405 }
406
407 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
408     scoped_ptr<base::DictionaryValue> response,
409     const DaemonController::UsageStatsConsent& consent) {
410   DCHECK(thread_checker_.CalledOnValidThread());
411
412   response->SetBoolean("supported", consent.supported);
413   response->SetBoolean("allowed", consent.allowed);
414   response->SetBoolean("setByPolicy", consent.set_by_policy);
415   channel_->SendMessage(response.Pass());
416 }
417
418 void Me2MeNativeMessagingHost::SendAsyncResult(
419     scoped_ptr<base::DictionaryValue> response,
420     DaemonController::AsyncResult result) {
421   DCHECK(thread_checker_.CalledOnValidThread());
422
423   switch (result) {
424     case DaemonController::RESULT_OK:
425       response->SetString("result", "OK");
426       break;
427     case DaemonController::RESULT_FAILED:
428       response->SetString("result", "FAILED");
429       break;
430     case DaemonController::RESULT_CANCELLED:
431       response->SetString("result", "CANCELLED");
432       break;
433     case DaemonController::RESULT_FAILED_DIRECTORY:
434       response->SetString("result", "FAILED_DIRECTORY");
435       break;
436   }
437   channel_->SendMessage(response.Pass());
438 }
439
440 void Me2MeNativeMessagingHost::SendBooleanResult(
441     scoped_ptr<base::DictionaryValue> response, bool result) {
442   DCHECK(thread_checker_.CalledOnValidThread());
443
444   response->SetBoolean("result", result);
445   channel_->SendMessage(response.Pass());
446 }
447
448 void Me2MeNativeMessagingHost::SendCredentialsResponse(
449     scoped_ptr<base::DictionaryValue> response,
450     const std::string& user_email,
451     const std::string& refresh_token) {
452   DCHECK(thread_checker_.CalledOnValidThread());
453
454   response->SetString("userEmail", user_email);
455   response->SetString("refreshToken", refresh_token);
456   channel_->SendMessage(response.Pass());
457 }
458
459 void Me2MeNativeMessagingHost::OnError() {
460   // Trigger a host shutdown by sending a NULL message.
461   channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
462 }
463
464 }  // namespace remoting