6acb7362a5a8f10a9ad200beed928294204542b4
[platform/framework/web/crosswalk.git] / src / remoting / host / plugin / host_script_object.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/plugin/host_script_object.h"
6
7 #include "base/bind.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "remoting/base/auth_token_util.h"
15 #include "remoting/base/auto_thread.h"
16 #include "remoting/base/logging.h"
17 #include "remoting/base/resources.h"
18 #include "remoting/base/rsa_key_pair.h"
19 #include "remoting/host/chromoting_host_context.h"
20 #include "remoting/host/host_config.h"
21 #include "remoting/host/pairing_registry_delegate.h"
22 #include "remoting/host/pin_hash.h"
23 #include "remoting/host/plugin/host_log_handler.h"
24 #include "remoting/host/policy_hack/policy_watcher.h"
25 #include "remoting/host/service_urls.h"
26 #include "third_party/npapi/bindings/npapi.h"
27 #include "third_party/npapi/bindings/npfunctions.h"
28 #include "third_party/npapi/bindings/npruntime.h"
29
30 namespace remoting {
31
32 namespace {
33
34 const char* kAttrNameAccessCode = "accessCode";
35 const char* kAttrNameAccessCodeLifetime = "accessCodeLifetime";
36 const char* kAttrNameClient = "client";
37 const char* kAttrNameDaemonState = "daemonState";
38 const char* kAttrNameState = "state";
39 const char* kAttrNameLogDebugInfo = "logDebugInfo";
40 const char* kAttrNameOnNatTraversalPolicyChanged =
41     "onNatTraversalPolicyChanged";
42 const char* kAttrNameOnStateChanged = "onStateChanged";
43 const char* kAttrNameXmppServerAddress = "xmppServerAddress";
44 const char* kAttrNameXmppServerUseTls = "xmppServerUseTls";
45 const char* kAttrNameDirectoryBotJid = "directoryBotJid";
46 const char* kAttrNameSupportedFeatures = "supportedFeatures";
47 const char* kFuncNameConnect = "connect";
48 const char* kFuncNameDisconnect = "disconnect";
49 const char* kFuncNameLocalize = "localize";
50 const char* kFuncNameClearPairedClients = "clearPairedClients";
51 const char* kFuncNameDeletePairedClient = "deletePairedClient";
52 const char* kFuncNameGetHostName = "getHostName";
53 const char* kFuncNameGetPinHash = "getPinHash";
54 const char* kFuncNameGenerateKeyPair = "generateKeyPair";
55 const char* kFuncNameUpdateDaemonConfig = "updateDaemonConfig";
56 const char* kFuncNameGetDaemonConfig = "getDaemonConfig";
57 const char* kFuncNameGetDaemonVersion = "getDaemonVersion";
58 const char* kFuncNameGetPairedClients = "getPairedClients";
59 const char* kFuncNameGetUsageStatsConsent = "getUsageStatsConsent";
60 const char* kFuncNameInstallHost = "installHost";
61 const char* kFuncNameStartDaemon = "startDaemon";
62 const char* kFuncNameStopDaemon = "stopDaemon";
63
64 // States.
65 const char* kAttrNameDisconnected = "DISCONNECTED";
66 const char* kAttrNameStarting = "STARTING";
67 const char* kAttrNameRequestedAccessCode = "REQUESTED_ACCESS_CODE";
68 const char* kAttrNameReceivedAccessCode = "RECEIVED_ACCESS_CODE";
69 const char* kAttrNameConnected = "CONNECTED";
70 const char* kAttrNameDisconnecting = "DISCONNECTING";
71 const char* kAttrNameError = "ERROR";
72 const char* kAttrNameInvalidDomainError = "INVALID_DOMAIN_ERROR";
73
74 // Space separated list of features supported in addition to the base protocol.
75 const char* kSupportedFeatures = "pairingRegistry";
76
77 }  // namespace
78
79 HostNPScriptObject::HostNPScriptObject(
80     NPP plugin,
81     NPObject* parent,
82     scoped_refptr<AutoThreadTaskRunner> plugin_task_runner)
83     : plugin_(plugin),
84       parent_(parent),
85       plugin_task_runner_(plugin_task_runner),
86       am_currently_logging_(false),
87       state_(kDisconnected),
88       weak_factory_(this) {
89   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
90
91   weak_ptr_ = weak_factory_.GetWeakPtr();
92
93   // Set the thread task runner for the plugin thread so that timers and other
94   // code using |base::ThreadTaskRunnerHandle| could be used on the plugin
95   // thread.
96   //
97   // If component build is used, Chrome and the plugin may end up sharing base
98   // binary. This means that the instance of |base::ThreadTaskRunnerHandle|
99   // created by Chrome for the current thread is shared as well. This routinely
100   // happens in the development setting so the below check for
101   // |!base::ThreadTaskRunnerHandle::IsSet()| is a hack/workaround allowing this
102   // configuration to work. It lets the plugin to access Chrome's message loop
103   // directly via |base::ThreadTaskRunnerHandle|. This is safe as long as both
104   // Chrome and the plugin are built from the same version of the sources.
105   if (!base::ThreadTaskRunnerHandle::IsSet()) {
106     plugin_task_runner_handle_.reset(
107         new base::ThreadTaskRunnerHandle(plugin_task_runner_));
108   }
109
110   daemon_controller_ = DaemonController::Create();
111
112   ServiceUrls* service_urls = ServiceUrls::GetInstance();
113   bool xmpp_server_valid = net::ParseHostAndPort(
114       service_urls->xmpp_server_address(),
115       &xmpp_server_config_.host, &xmpp_server_config_.port);
116   // For the plugin, this is always the default address, which must be valid.
117   DCHECK(xmpp_server_valid);
118   xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
119   directory_bot_jid_ = service_urls->directory_bot_jid();
120
121   // Create worker thread for encryption key generation and loading the paired
122   // clients.
123   worker_thread_ = AutoThread::Create("ChromotingWorkerThread",
124                                       plugin_task_runner_);
125
126   pairing_registry_ = CreatePairingRegistry(worker_thread_);
127 }
128
129 HostNPScriptObject::~HostNPScriptObject() {
130   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
131
132   HostLogHandler::UnregisterLoggingScriptObject(this);
133
134   // Stop the It2Me host if the caller forgot to.
135   if (it2me_host_.get()) {
136     it2me_host_->Disconnect();
137     it2me_host_ = NULL;
138   }
139 }
140
141 bool HostNPScriptObject::HasMethod(const std::string& method_name) {
142   VLOG(2) << "HasMethod " << method_name;
143   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
144   return (method_name == kFuncNameConnect ||
145           method_name == kFuncNameDisconnect ||
146           method_name == kFuncNameLocalize ||
147           method_name == kFuncNameClearPairedClients ||
148           method_name == kFuncNameDeletePairedClient ||
149           method_name == kFuncNameGetHostName ||
150           method_name == kFuncNameGetPinHash ||
151           method_name == kFuncNameGenerateKeyPair ||
152           method_name == kFuncNameUpdateDaemonConfig ||
153           method_name == kFuncNameGetDaemonConfig ||
154           method_name == kFuncNameGetDaemonVersion ||
155           method_name == kFuncNameGetPairedClients ||
156           method_name == kFuncNameGetUsageStatsConsent ||
157           method_name == kFuncNameInstallHost ||
158           method_name == kFuncNameStartDaemon ||
159           method_name == kFuncNameStopDaemon);
160 }
161
162 bool HostNPScriptObject::InvokeDefault(const NPVariant* args,
163                                        uint32_t arg_count,
164                                        NPVariant* result) {
165   VLOG(2) << "InvokeDefault";
166   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
167   SetException("exception during default invocation");
168   return false;
169 }
170
171 bool HostNPScriptObject::Invoke(const std::string& method_name,
172                                 const NPVariant* args,
173                                 uint32_t arg_count,
174                                 NPVariant* result) {
175   VLOG(2) << "Invoke " << method_name;
176   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
177   if (method_name == kFuncNameConnect) {
178     return Connect(args, arg_count, result);
179   } else if (method_name == kFuncNameDisconnect) {
180     return Disconnect(args, arg_count, result);
181   } else if (method_name == kFuncNameLocalize) {
182     return Localize(args, arg_count, result);
183   } else if (method_name == kFuncNameClearPairedClients) {
184     return ClearPairedClients(args, arg_count, result);
185   } else if (method_name == kFuncNameDeletePairedClient) {
186     return DeletePairedClient(args, arg_count, result);
187   } else if (method_name == kFuncNameGetHostName) {
188     return GetHostName(args, arg_count, result);
189   } else if (method_name == kFuncNameGetPinHash) {
190     return GetPinHash(args, arg_count, result);
191   } else if (method_name == kFuncNameGenerateKeyPair) {
192     return GenerateKeyPair(args, arg_count, result);
193   } else if (method_name == kFuncNameUpdateDaemonConfig) {
194     return UpdateDaemonConfig(args, arg_count, result);
195   } else if (method_name == kFuncNameGetDaemonConfig) {
196     return GetDaemonConfig(args, arg_count, result);
197   } else if (method_name == kFuncNameGetDaemonVersion) {
198     return GetDaemonVersion(args, arg_count, result);
199   } else if (method_name == kFuncNameGetPairedClients) {
200     return GetPairedClients(args, arg_count, result);
201   } else if (method_name == kFuncNameGetUsageStatsConsent) {
202     return GetUsageStatsConsent(args, arg_count, result);
203   } else if (method_name == kFuncNameInstallHost) {
204     return InstallHost(args, arg_count, result);
205   } else if (method_name == kFuncNameStartDaemon) {
206     return StartDaemon(args, arg_count, result);
207   } else if (method_name == kFuncNameStopDaemon) {
208     return StopDaemon(args, arg_count, result);
209   } else {
210     SetException("Invoke: unknown method " + method_name);
211     return false;
212   }
213 }
214
215 bool HostNPScriptObject::HasProperty(const std::string& property_name) {
216   VLOG(2) << "HasProperty " << property_name;
217   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
218   return (property_name == kAttrNameAccessCode ||
219           property_name == kAttrNameAccessCodeLifetime ||
220           property_name == kAttrNameClient ||
221           property_name == kAttrNameDaemonState ||
222           property_name == kAttrNameState ||
223           property_name == kAttrNameLogDebugInfo ||
224           property_name == kAttrNameOnNatTraversalPolicyChanged ||
225           property_name == kAttrNameOnStateChanged ||
226           property_name == kAttrNameDisconnected ||
227           property_name == kAttrNameStarting ||
228           property_name == kAttrNameRequestedAccessCode ||
229           property_name == kAttrNameReceivedAccessCode ||
230           property_name == kAttrNameConnected ||
231           property_name == kAttrNameDisconnecting ||
232           property_name == kAttrNameError ||
233           property_name == kAttrNameXmppServerAddress ||
234           property_name == kAttrNameXmppServerUseTls ||
235           property_name == kAttrNameDirectoryBotJid ||
236           property_name == kAttrNameSupportedFeatures);
237 }
238
239 bool HostNPScriptObject::GetProperty(const std::string& property_name,
240                                      NPVariant* result) {
241   VLOG(2) << "GetProperty " << property_name;
242   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
243   if (!result) {
244     SetException("GetProperty: NULL result");
245     return false;
246   }
247
248   if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
249     OBJECT_TO_NPVARIANT(on_nat_traversal_policy_changed_func_.get(), *result);
250     return true;
251   } else if (property_name == kAttrNameOnStateChanged) {
252     OBJECT_TO_NPVARIANT(on_state_changed_func_.get(), *result);
253     return true;
254   } else if (property_name == kAttrNameLogDebugInfo) {
255     OBJECT_TO_NPVARIANT(log_debug_info_func_.get(), *result);
256     return true;
257   } else if (property_name == kAttrNameState) {
258     INT32_TO_NPVARIANT(state_, *result);
259     return true;
260   } else if (property_name == kAttrNameAccessCode) {
261     *result = NPVariantFromString(access_code_);
262     return true;
263   } else if (property_name == kAttrNameAccessCodeLifetime) {
264     INT32_TO_NPVARIANT(access_code_lifetime_.InSeconds(), *result);
265     return true;
266   } else if (property_name == kAttrNameClient) {
267     *result = NPVariantFromString(client_username_);
268     return true;
269   } else if (property_name == kAttrNameDaemonState) {
270     INT32_TO_NPVARIANT(daemon_controller_->GetState(), *result);
271     return true;
272   } else if (property_name == kAttrNameDisconnected) {
273     INT32_TO_NPVARIANT(kDisconnected, *result);
274     return true;
275   } else if (property_name == kAttrNameStarting) {
276     INT32_TO_NPVARIANT(kStarting, *result);
277     return true;
278   } else if (property_name == kAttrNameRequestedAccessCode) {
279     INT32_TO_NPVARIANT(kRequestedAccessCode, *result);
280     return true;
281   } else if (property_name == kAttrNameReceivedAccessCode) {
282     INT32_TO_NPVARIANT(kReceivedAccessCode, *result);
283     return true;
284   } else if (property_name == kAttrNameConnected) {
285     INT32_TO_NPVARIANT(kConnected, *result);
286     return true;
287   } else if (property_name == kAttrNameDisconnecting) {
288     INT32_TO_NPVARIANT(kDisconnecting, *result);
289     return true;
290   } else if (property_name == kAttrNameError) {
291     INT32_TO_NPVARIANT(kError, *result);
292     return true;
293   } else if (property_name == kAttrNameInvalidDomainError) {
294     INT32_TO_NPVARIANT(kInvalidDomainError, *result);
295     return true;
296   } else if (property_name == kAttrNameXmppServerAddress) {
297     *result = NPVariantFromString(base::StringPrintf(
298         "%s:%u", xmpp_server_config_.host.c_str(), xmpp_server_config_.port));
299     return true;
300   } else if (property_name == kAttrNameXmppServerUseTls) {
301     BOOLEAN_TO_NPVARIANT(xmpp_server_config_.use_tls, *result);
302     return true;
303   } else if (property_name == kAttrNameDirectoryBotJid) {
304     *result = NPVariantFromString(directory_bot_jid_);
305     return true;
306   } else if (property_name == kAttrNameSupportedFeatures) {
307     *result = NPVariantFromString(kSupportedFeatures);
308     return true;
309   } else {
310     SetException("GetProperty: unsupported property " + property_name);
311     return false;
312   }
313 }
314
315 bool HostNPScriptObject::SetProperty(const std::string& property_name,
316                                      const NPVariant* value) {
317   VLOG(2) <<  "SetProperty " << property_name;
318   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
319
320   if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
321     if (NPVARIANT_IS_OBJECT(*value)) {
322       on_nat_traversal_policy_changed_func_ = NPVARIANT_TO_OBJECT(*value);
323       if (it2me_host_.get()) {
324         // Ask the It2Me host to notify the web-app of the policy.
325         it2me_host_->RequestNatPolicy();
326       }
327       return true;
328     } else {
329       SetException("SetProperty: unexpected type for property " +
330                    property_name);
331     }
332     return false;
333   }
334
335   if (property_name == kAttrNameOnStateChanged) {
336     if (NPVARIANT_IS_OBJECT(*value)) {
337       on_state_changed_func_ = NPVARIANT_TO_OBJECT(*value);
338       return true;
339     } else {
340       SetException("SetProperty: unexpected type for property " +
341                    property_name);
342     }
343     return false;
344   }
345
346   if (property_name == kAttrNameLogDebugInfo) {
347     if (NPVARIANT_IS_OBJECT(*value)) {
348       log_debug_info_func_ = NPVARIANT_TO_OBJECT(*value);
349       HostLogHandler::RegisterLoggingScriptObject(this);
350       return true;
351     } else {
352       SetException("SetProperty: unexpected type for property " +
353                    property_name);
354     }
355     return false;
356   }
357
358 #if !defined(NDEBUG)
359   if (property_name == kAttrNameXmppServerAddress) {
360     if (NPVARIANT_IS_STRING(*value)) {
361       std::string address = StringFromNPVariant(*value);
362       bool xmpp_server_valid = net::ParseHostAndPort(
363           address, &xmpp_server_config_.host, &xmpp_server_config_.port);
364       if (xmpp_server_valid) {
365         return true;
366       } else {
367         SetException("SetProperty: invalid value for property " +
368                      property_name);
369       }
370     } else {
371       SetException("SetProperty: unexpected type for property " +
372                    property_name);
373     }
374     return false;
375   }
376
377   if (property_name == kAttrNameXmppServerUseTls) {
378     if (NPVARIANT_IS_BOOLEAN(*value)) {
379       xmpp_server_config_.use_tls = NPVARIANT_TO_BOOLEAN(*value);
380       return true;
381     } else {
382       SetException("SetProperty: unexpected type for property " +
383                    property_name);
384     }
385     return false;
386   }
387
388   if (property_name == kAttrNameDirectoryBotJid) {
389     if (NPVARIANT_IS_STRING(*value)) {
390       directory_bot_jid_ = StringFromNPVariant(*value);
391       return true;
392     } else {
393       SetException("SetProperty: unexpected type for property " +
394                    property_name);
395     }
396     return false;
397   }
398 #endif  // !defined(NDEBUG)
399
400   return false;
401 }
402
403 bool HostNPScriptObject::RemoveProperty(const std::string& property_name) {
404   VLOG(2) << "RemoveProperty " << property_name;
405   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
406   return false;
407 }
408
409 bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) {
410   VLOG(2) << "Enumerate";
411   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
412   const char* entries[] = {
413     kAttrNameAccessCode,
414     kAttrNameState,
415     kAttrNameLogDebugInfo,
416     kAttrNameOnStateChanged,
417     kAttrNameDisconnected,
418     kAttrNameStarting,
419     kAttrNameRequestedAccessCode,
420     kAttrNameReceivedAccessCode,
421     kAttrNameConnected,
422     kAttrNameDisconnecting,
423     kAttrNameError,
424     kAttrNameXmppServerAddress,
425     kAttrNameXmppServerUseTls,
426     kAttrNameDirectoryBotJid,
427     kFuncNameConnect,
428     kFuncNameDisconnect,
429     kFuncNameLocalize,
430     kFuncNameClearPairedClients,
431     kFuncNameDeletePairedClient,
432     kFuncNameGetHostName,
433     kFuncNameGetPinHash,
434     kFuncNameGenerateKeyPair,
435     kFuncNameUpdateDaemonConfig,
436     kFuncNameGetDaemonConfig,
437     kFuncNameGetDaemonVersion,
438     kFuncNameGetPairedClients,
439     kFuncNameGetUsageStatsConsent,
440     kFuncNameInstallHost,
441     kFuncNameStartDaemon,
442     kFuncNameStopDaemon
443   };
444   for (size_t i = 0; i < arraysize(entries); ++i) {
445     values->push_back(entries[i]);
446   }
447   return true;
448 }
449
450 // string username, string auth_token
451 bool HostNPScriptObject::Connect(const NPVariant* args,
452                                  uint32_t arg_count,
453                                  NPVariant* result) {
454   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
455
456   HOST_LOG << "Connecting...";
457
458   if (arg_count != 2) {
459     SetException("connect: bad number of arguments");
460     return false;
461   }
462
463   if (it2me_host_.get()) {
464     SetException("connect: can be called only when disconnected");
465     return false;
466   }
467
468   XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_;
469
470   xmpp_config.username = StringFromNPVariant(args[0]);
471   if (xmpp_config.username.empty()) {
472     SetException("connect: bad username argument");
473     return false;
474   }
475
476   std::string auth_service_with_token = StringFromNPVariant(args[1]);
477   ParseAuthTokenWithService(auth_service_with_token, &xmpp_config.auth_token,
478                             &xmpp_config.auth_service);
479   if (xmpp_config.auth_token.empty()) {
480     SetException("connect: auth_service_with_token argument has empty token");
481     return false;
482   }
483
484   // Create a host context to manage the threads for the it2me host.
485   // The plugin, rather than the It2MeHost object, owns and maintains the
486   // lifetime of the host context.
487   host_context_.reset(
488       ChromotingHostContext::Create(plugin_task_runner_).release());
489   if (!host_context_) {
490     SetException("connect: failed to start threads");
491     return false;
492   }
493
494   // Create the It2Me host and start connecting.
495   scoped_ptr<It2MeHostFactory> factory(new It2MeHostFactory());
496   it2me_host_ = factory->CreateIt2MeHost(
497       host_context_.get(), plugin_task_runner_, weak_ptr_,
498       xmpp_config, directory_bot_jid_);
499   it2me_host_->Connect();
500
501   return true;
502 }
503
504 bool HostNPScriptObject::Disconnect(const NPVariant* args,
505                                     uint32_t arg_count,
506                                     NPVariant* result) {
507   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
508   if (arg_count != 0) {
509     SetException("disconnect: bad number of arguments");
510     return false;
511   }
512
513   if (it2me_host_.get()) {
514     it2me_host_->Disconnect();
515     it2me_host_ = NULL;
516   }
517
518   return true;
519 }
520
521 bool HostNPScriptObject::Localize(const NPVariant* args,
522                                   uint32_t arg_count,
523                                   NPVariant* result) {
524   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
525   if (arg_count != 1) {
526     SetException("localize: bad number of arguments");
527     return false;
528   }
529
530   if (NPVARIANT_IS_OBJECT(args[0])) {
531     ScopedRefNPObject localize_func(NPVARIANT_TO_OBJECT(args[0]));
532     LocalizeStrings(localize_func.get());
533     return true;
534   } else {
535     SetException("localize: unexpected type for argument 1");
536     return false;
537   }
538 }
539
540 bool HostNPScriptObject::ClearPairedClients(const NPVariant* args,
541                                             uint32_t arg_count,
542                                             NPVariant* result) {
543   if (arg_count != 1) {
544     SetException("clearPairedClients: bad number of arguments");
545     return false;
546   }
547
548   if (!NPVARIANT_IS_OBJECT(args[0])) {
549     SetException("clearPairedClients: invalid callback parameter");
550     return false;
551   }
552
553   scoped_ptr<ScopedRefNPObject> callback_obj(
554       new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
555   if (pairing_registry_) {
556     pairing_registry_->ClearAllPairings(
557         base::Bind(&HostNPScriptObject::InvokeBooleanCallback, weak_ptr_,
558                    base::Passed(&callback_obj)));
559   } else {
560     InvokeBooleanCallback(callback_obj.Pass(), false);
561   }
562
563   return true;
564 }
565
566 bool HostNPScriptObject::DeletePairedClient(const NPVariant* args,
567                                             uint32_t arg_count,
568                                             NPVariant* result) {
569   if (arg_count != 2) {
570     SetException("deletePairedClient: bad number of arguments");
571     return false;
572   }
573
574   if (!NPVARIANT_IS_STRING(args[0])) {
575     SetException("deletePairedClient: bad clientId parameter");
576     return false;
577   }
578
579   if (!NPVARIANT_IS_OBJECT(args[1])) {
580     SetException("deletePairedClient: invalid callback parameter");
581     return false;
582   }
583
584   std::string client_id = StringFromNPVariant(args[0]);
585   scoped_ptr<ScopedRefNPObject> callback_obj(
586       new ScopedRefNPObject(ObjectFromNPVariant(args[1])));
587   if (pairing_registry_) {
588     pairing_registry_->DeletePairing(
589         client_id,
590         base::Bind(&HostNPScriptObject::InvokeBooleanCallback,
591                    weak_ptr_, base::Passed(&callback_obj)));
592   } else {
593     InvokeBooleanCallback(callback_obj.Pass(), false);
594   }
595
596   return true;
597 }
598
599 bool HostNPScriptObject::GetHostName(const NPVariant* args,
600                                      uint32_t arg_count,
601                                      NPVariant* result) {
602   if (arg_count != 1) {
603     SetException("getHostName: bad number of arguments");
604     return false;
605   }
606
607   ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0]));
608   if (!callback_obj.get()) {
609     SetException("getHostName: invalid callback parameter");
610     return false;
611   }
612
613   NPVariant host_name_val = NPVariantFromString(net::GetHostName());
614   InvokeAndIgnoreResult(callback_obj, &host_name_val, 1);
615   g_npnetscape_funcs->releasevariantvalue(&host_name_val);
616
617   return true;
618 }
619
620 bool HostNPScriptObject::GetPinHash(const NPVariant* args,
621                                     uint32_t arg_count,
622                                     NPVariant* result) {
623   if (arg_count != 3) {
624     SetException("getPinHash: bad number of arguments");
625     return false;
626   }
627
628   std::string host_id = StringFromNPVariant(args[0]);
629   if (host_id.empty()) {
630     SetException("getPinHash: bad hostId parameter");
631     return false;
632   }
633
634   if (!NPVARIANT_IS_STRING(args[1])) {
635     SetException("getPinHash: bad pin parameter");
636     return false;
637   }
638   std::string pin = StringFromNPVariant(args[1]);
639
640   ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[2]));
641   if (!callback_obj.get()) {
642     SetException("getPinHash: invalid callback parameter");
643     return false;
644   }
645
646   NPVariant pin_hash_val = NPVariantFromString(
647       remoting::MakeHostPinHash(host_id, pin));
648   InvokeAndIgnoreResult(callback_obj, &pin_hash_val, 1);
649   g_npnetscape_funcs->releasevariantvalue(&pin_hash_val);
650
651   return true;
652 }
653
654 bool HostNPScriptObject::GenerateKeyPair(const NPVariant* args,
655                                          uint32_t arg_count,
656                                          NPVariant* result) {
657   if (arg_count != 1) {
658     SetException("generateKeyPair: bad number of arguments");
659     return false;
660   }
661
662   scoped_ptr<ScopedRefNPObject> callback_obj(
663       new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
664   if (!callback_obj->get()) {
665     SetException("generateKeyPair: invalid callback parameter");
666     return false;
667   }
668
669   base::Callback<void (const std::string&,
670                        const std::string&)> wrapped_callback =
671       base::Bind(&HostNPScriptObject::InvokeGenerateKeyPairCallback, weak_ptr_,
672                  base::Passed(&callback_obj));
673   worker_thread_->PostTask(
674       FROM_HERE, base::Bind(&HostNPScriptObject::DoGenerateKeyPair,
675                             plugin_task_runner_, wrapped_callback));
676   return true;
677 }
678
679 bool HostNPScriptObject::UpdateDaemonConfig(const NPVariant* args,
680                                             uint32_t arg_count,
681                                             NPVariant* result) {
682   if (arg_count != 2) {
683     SetException("updateDaemonConfig: bad number of arguments");
684     return false;
685   }
686
687   std::string config_str = StringFromNPVariant(args[0]);
688   scoped_ptr<base::Value> config(
689       base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS));
690   if (config_str.empty() || !config.get() ||
691       !config->IsType(base::Value::TYPE_DICTIONARY)) {
692     SetException("updateDaemonConfig: bad config parameter");
693     return false;
694   }
695   scoped_ptr<base::DictionaryValue> config_dict(
696       reinterpret_cast<base::DictionaryValue*>(config.release()));
697
698   scoped_ptr<ScopedRefNPObject> callback_obj(
699       new ScopedRefNPObject(ObjectFromNPVariant(args[1])));
700   if (!callback_obj->get()) {
701     SetException("updateDaemonConfig: invalid callback parameter");
702     return false;
703   }
704
705   if (config_dict->HasKey(kHostIdConfigPath) ||
706       config_dict->HasKey(kXmppLoginConfigPath)) {
707     SetException("updateDaemonConfig: trying to update immutable config "
708                  "parameters");
709     return false;
710   }
711
712   daemon_controller_->UpdateConfig(
713       config_dict.Pass(),
714       base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
715                  base::Passed(&callback_obj)));
716   return true;
717 }
718
719 bool HostNPScriptObject::GetDaemonConfig(const NPVariant* args,
720                                          uint32_t arg_count,
721                                          NPVariant* result) {
722   if (arg_count != 1) {
723     SetException("getDaemonConfig: bad number of arguments");
724     return false;
725   }
726
727   scoped_ptr<ScopedRefNPObject> callback_obj(
728       new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
729   if (!callback_obj->get()) {
730     SetException("getDaemonConfig: invalid callback parameter");
731     return false;
732   }
733
734   daemon_controller_->GetConfig(
735       base::Bind(&HostNPScriptObject::InvokeGetDaemonConfigCallback, weak_ptr_,
736                  base::Passed(&callback_obj)));
737   return true;
738 }
739
740 bool HostNPScriptObject::GetDaemonVersion(const NPVariant* args,
741                                           uint32_t arg_count,
742                                           NPVariant* result) {
743   if (arg_count != 1) {
744     SetException("getDaemonVersion: bad number of arguments");
745     return false;
746   }
747
748   scoped_ptr<ScopedRefNPObject> callback_obj(
749       new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
750   if (!callback_obj->get()) {
751     SetException("getDaemonVersion: invalid callback parameter");
752     return false;
753   }
754
755   daemon_controller_->GetVersion(
756       base::Bind(&HostNPScriptObject::InvokeGetDaemonVersionCallback, weak_ptr_,
757                  base::Passed(&callback_obj)));
758
759   return true;
760 }
761
762 bool HostNPScriptObject::GetPairedClients(const NPVariant* args,
763                                           uint32_t arg_count,
764                                           NPVariant* result) {
765   if (arg_count != 1) {
766     SetException("getPairedClients: bad number of arguments");
767     return false;
768   }
769
770   scoped_ptr<ScopedRefNPObject> callback_obj(
771       new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
772   if (!callback_obj->get()) {
773     SetException("getPairedClients: invalid callback parameter");
774     return false;
775   }
776
777   if (pairing_registry_) {
778     pairing_registry_->GetAllPairings(
779         base::Bind(&HostNPScriptObject::InvokeGetPairedClientsCallback,
780                    weak_ptr_, base::Passed(&callback_obj)));
781   } else {
782     scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
783     InvokeGetPairedClientsCallback(callback_obj.Pass(),
784                                    no_paired_clients.Pass());
785   }
786   return true;
787 }
788
789 bool HostNPScriptObject::GetUsageStatsConsent(const NPVariant* args,
790                                               uint32_t arg_count,
791                                               NPVariant* result) {
792   if (arg_count != 1) {
793     SetException("getUsageStatsConsent: bad number of arguments");
794     return false;
795   }
796
797   scoped_ptr<ScopedRefNPObject> callback_obj(
798       new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
799   if (!callback_obj->get()) {
800     SetException("getUsageStatsConsent: invalid callback parameter");
801     return false;
802   }
803
804   daemon_controller_->GetUsageStatsConsent(
805       base::Bind(&HostNPScriptObject::InvokeGetUsageStatsConsentCallback,
806                  weak_ptr_, base::Passed(&callback_obj)));
807   return true;
808 }
809
810 bool HostNPScriptObject::InstallHost(const NPVariant* args,
811                                      uint32_t arg_count,
812                                      NPVariant* result) {
813   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
814
815   if (arg_count != 1) {
816     SetException("installHost: bad number of arguments");
817     return false;
818   }
819
820   scoped_ptr<ScopedRefNPObject> callback_obj(
821       new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
822   if (!callback_obj->get()) {
823     SetException("installHost: invalid callback parameter");
824     return false;
825   }
826
827   daemon_controller_->InstallHost(
828       base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
829                  base::Passed(&callback_obj)));
830   return true;
831 }
832
833 bool HostNPScriptObject::StartDaemon(const NPVariant* args,
834                                      uint32_t arg_count,
835                                      NPVariant* result) {
836   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
837
838   if (arg_count != 3) {
839     SetException("startDaemon: bad number of arguments");
840     return false;
841   }
842
843   std::string config_str = StringFromNPVariant(args[0]);
844   scoped_ptr<base::Value> config(
845       base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS));
846   if (config_str.empty() || !config.get() ||
847       !config->IsType(base::Value::TYPE_DICTIONARY)) {
848     SetException("startDaemon: bad config parameter");
849     return false;
850   }
851   scoped_ptr<base::DictionaryValue> config_dict(
852       reinterpret_cast<base::DictionaryValue*>(config.release()));
853
854   if (!NPVARIANT_IS_BOOLEAN(args[1])) {
855     SetException("startDaemon: invalid consent parameter");
856     return false;
857   }
858
859   scoped_ptr<ScopedRefNPObject> callback_obj(
860       new ScopedRefNPObject(ObjectFromNPVariant(args[2])));
861   if (!callback_obj->get()) {
862     SetException("startDaemon: invalid callback parameter");
863     return false;
864   }
865
866   daemon_controller_->SetConfigAndStart(
867       config_dict.Pass(),
868       NPVARIANT_TO_BOOLEAN(args[1]),
869       base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
870                  base::Passed(&callback_obj)));
871   return true;
872 }
873
874 bool HostNPScriptObject::StopDaemon(const NPVariant* args,
875                                     uint32_t arg_count,
876                                     NPVariant* result) {
877   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
878
879   if (arg_count != 1) {
880     SetException("stopDaemon: bad number of arguments");
881     return false;
882   }
883
884   scoped_ptr<ScopedRefNPObject> callback_obj(
885       new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
886   if (!callback_obj->get()) {
887     SetException("stopDaemon: invalid callback parameter");
888     return false;
889   }
890
891   daemon_controller_->Stop(
892       base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
893                  base::Passed(&callback_obj)));
894   return true;
895 }
896
897 void HostNPScriptObject::OnStateChanged(It2MeHostState state) {
898   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
899
900   state_ = state;
901
902   if (state_ == kDisconnected)
903     client_username_.clear();
904
905   if (on_state_changed_func_.get()) {
906     NPVariant state_var;
907     INT32_TO_NPVARIANT(state, state_var);
908     InvokeAndIgnoreResult(on_state_changed_func_, &state_var, 1);
909   }
910 }
911
912 void HostNPScriptObject::OnNatPolicyChanged(bool nat_traversal_enabled) {
913   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
914
915   if (on_nat_traversal_policy_changed_func_.get()) {
916     NPVariant policy;
917     BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy);
918     InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_,
919                           &policy, 1);
920   }
921 }
922
923 // Stores the Access Code for the web-app to query.
924 void HostNPScriptObject::OnStoreAccessCode(
925     const std::string& access_code, base::TimeDelta access_code_lifetime) {
926   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
927
928   access_code_ = access_code;
929   access_code_lifetime_ = access_code_lifetime;
930 }
931
932 // Stores the client user's name for the web-app to query.
933 void HostNPScriptObject::OnClientAuthenticated(
934     const std::string& client_username) {
935   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
936
937   client_username_ = client_username;
938 }
939
940 void HostNPScriptObject::PostLogDebugInfo(const std::string& message) {
941   if (plugin_task_runner_->BelongsToCurrentThread()) {
942     // Make sure we're not currently processing a log message.
943     // We only need to check this if we're on the plugin thread.
944     if (am_currently_logging_)
945       return;
946   }
947
948   // Always post (even if we're already on the correct thread) so that debug
949   // log messages are shown in the correct order.
950   plugin_task_runner_->PostTask(
951       FROM_HERE, base::Bind(&HostNPScriptObject::LogDebugInfo,
952                             weak_ptr_, message));
953 }
954
955 void HostNPScriptObject::SetWindow(NPWindow* np_window) {
956   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
957
958   daemon_controller_->SetWindow(np_window->window);
959 }
960
961 void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) {
962   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
963
964   // Reload resources for the current locale. The default UI locale is used on
965   // Windows.
966 #if !defined(OS_WIN)
967   base::string16 ui_locale;
968   LocalizeString(localize_func, "@@ui_locale", &ui_locale);
969   remoting::LoadResources(base::UTF16ToUTF8(ui_locale));
970 #endif  // !defined(OS_WIN)
971 }
972
973 bool HostNPScriptObject::LocalizeString(NPObject* localize_func,
974                                         const char* tag,
975                                         base::string16* result) {
976   return LocalizeStringWithSubstitution(localize_func, tag, NULL, result);
977 }
978
979 bool HostNPScriptObject::LocalizeStringWithSubstitution(
980     NPObject* localize_func,
981     const char* tag,
982     const char* substitution,
983     base::string16* result) {
984   int argc = substitution ? 2 : 1;
985   scoped_ptr<NPVariant[]> args(new NPVariant[argc]);
986   STRINGZ_TO_NPVARIANT(tag, args[0]);
987   if (substitution) {
988     STRINGZ_TO_NPVARIANT(substitution, args[1]);
989   }
990   NPVariant np_result;
991   bool is_good = g_npnetscape_funcs->invokeDefault(
992       plugin_, localize_func, args.get(), argc, &np_result);
993   if (!is_good) {
994     LOG(ERROR) << "Localization failed for " << tag;
995     return false;
996   }
997   std::string translation = StringFromNPVariant(np_result);
998   g_npnetscape_funcs->releasevariantvalue(&np_result);
999   if (translation.empty()) {
1000     LOG(ERROR) << "Missing translation for " << tag;
1001     return false;
1002   }
1003   *result = base::UTF8ToUTF16(translation);
1004   return true;
1005 }
1006
1007 // static
1008 void HostNPScriptObject::DoGenerateKeyPair(
1009     const scoped_refptr<AutoThreadTaskRunner>& plugin_task_runner,
1010     const base::Callback<void (const std::string&,
1011                                const std::string&)>& callback) {
1012   scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
1013   plugin_task_runner->PostTask(FROM_HERE,
1014                                base::Bind(callback, key_pair->ToString(),
1015                                           key_pair->GetPublicKey()));
1016 }
1017
1018 void HostNPScriptObject::InvokeGenerateKeyPairCallback(
1019     scoped_ptr<ScopedRefNPObject> callback,
1020     const std::string& private_key,
1021     const std::string& public_key) {
1022   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1023
1024   NPVariant params[2];
1025   params[0] = NPVariantFromString(private_key);
1026   params[1] = NPVariantFromString(public_key);
1027   InvokeAndIgnoreResult(*callback, params, arraysize(params));
1028   g_npnetscape_funcs->releasevariantvalue(&(params[0]));
1029   g_npnetscape_funcs->releasevariantvalue(&(params[1]));
1030 }
1031
1032 void HostNPScriptObject::InvokeAsyncResultCallback(
1033     scoped_ptr<ScopedRefNPObject> callback,
1034     DaemonController::AsyncResult result) {
1035   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1036
1037   NPVariant result_var;
1038   INT32_TO_NPVARIANT(static_cast<int32>(result), result_var);
1039   InvokeAndIgnoreResult(*callback, &result_var, 1);
1040   g_npnetscape_funcs->releasevariantvalue(&result_var);
1041 }
1042
1043 void HostNPScriptObject::InvokeBooleanCallback(
1044     scoped_ptr<ScopedRefNPObject> callback, bool result) {
1045   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1046
1047   NPVariant result_var;
1048   BOOLEAN_TO_NPVARIANT(result, result_var);
1049   InvokeAndIgnoreResult(*callback, &result_var, 1);
1050   g_npnetscape_funcs->releasevariantvalue(&result_var);
1051 }
1052
1053 void HostNPScriptObject::InvokeGetDaemonConfigCallback(
1054     scoped_ptr<ScopedRefNPObject> callback,
1055     scoped_ptr<base::DictionaryValue> config) {
1056   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1057
1058   // There is no easy way to create a dictionary from an NPAPI plugin
1059   // so we have to serialize the dictionary to pass it to JavaScript.
1060   std::string config_str;
1061   if (config.get())
1062     base::JSONWriter::Write(config.get(), &config_str);
1063
1064   NPVariant config_val = NPVariantFromString(config_str);
1065   InvokeAndIgnoreResult(*callback, &config_val, 1);
1066   g_npnetscape_funcs->releasevariantvalue(&config_val);
1067 }
1068
1069 void HostNPScriptObject::InvokeGetDaemonVersionCallback(
1070     scoped_ptr<ScopedRefNPObject> callback, const std::string& version) {
1071   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1072
1073   NPVariant version_val = NPVariantFromString(version);
1074   InvokeAndIgnoreResult(*callback, &version_val, 1);
1075   g_npnetscape_funcs->releasevariantvalue(&version_val);
1076 }
1077
1078 void HostNPScriptObject::InvokeGetPairedClientsCallback(
1079     scoped_ptr<ScopedRefNPObject> callback,
1080     scoped_ptr<base::ListValue> paired_clients) {
1081   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1082
1083   std::string paired_clients_json;
1084   base::JSONWriter::Write(paired_clients.get(), &paired_clients_json);
1085
1086   NPVariant paired_clients_val = NPVariantFromString(paired_clients_json);
1087   InvokeAndIgnoreResult(*callback, &paired_clients_val, 1);
1088   g_npnetscape_funcs->releasevariantvalue(&paired_clients_val);
1089 }
1090
1091 void HostNPScriptObject::InvokeGetUsageStatsConsentCallback(
1092     scoped_ptr<ScopedRefNPObject> callback,
1093     const DaemonController::UsageStatsConsent& consent) {
1094   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1095
1096   NPVariant params[3];
1097   BOOLEAN_TO_NPVARIANT(consent.supported, params[0]);
1098   BOOLEAN_TO_NPVARIANT(consent.allowed, params[1]);
1099   BOOLEAN_TO_NPVARIANT(consent.set_by_policy, params[2]);
1100   InvokeAndIgnoreResult(*callback, params, arraysize(params));
1101   g_npnetscape_funcs->releasevariantvalue(&(params[0]));
1102   g_npnetscape_funcs->releasevariantvalue(&(params[1]));
1103   g_npnetscape_funcs->releasevariantvalue(&(params[2]));
1104 }
1105
1106 void HostNPScriptObject::LogDebugInfo(const std::string& message) {
1107   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1108
1109   if (log_debug_info_func_.get()) {
1110     am_currently_logging_ = true;
1111     NPVariant log_message;
1112     STRINGZ_TO_NPVARIANT(message.c_str(), log_message);
1113     bool is_good = InvokeAndIgnoreResult(log_debug_info_func_,
1114                                          &log_message, 1);
1115     if (!is_good) {
1116       LOG(ERROR) << "ERROR - LogDebugInfo failed\n";
1117     }
1118     am_currently_logging_ = false;
1119   }
1120 }
1121
1122 bool HostNPScriptObject::InvokeAndIgnoreResult(const ScopedRefNPObject& func,
1123                                                const NPVariant* args,
1124                                                uint32_t arg_count) {
1125   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1126
1127   NPVariant np_result;
1128   bool is_good = g_npnetscape_funcs->invokeDefault(plugin_, func.get(), args,
1129                                                    arg_count, &np_result);
1130   if (is_good)
1131       g_npnetscape_funcs->releasevariantvalue(&np_result);
1132
1133   return is_good;
1134 }
1135
1136 void HostNPScriptObject::SetException(const std::string& exception_string) {
1137   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1138
1139   g_npnetscape_funcs->setexception(parent_, exception_string.c_str());
1140   HOST_LOG << exception_string;
1141 }
1142
1143 }  // namespace remoting