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