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.
5 #include "remoting/host/plugin/host_script_object.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"
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";
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";
72 // Space separated list of features supported in addition to the base protocol.
73 const char* kSupportedFeatures = "pairingRegistry";
77 HostNPScriptObject::HostNPScriptObject(
80 scoped_refptr<AutoThreadTaskRunner> plugin_task_runner)
83 plugin_task_runner_(plugin_task_runner),
84 am_currently_logging_(false),
85 state_(kDisconnected),
87 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
89 weak_ptr_ = weak_factory_.GetWeakPtr();
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
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_));
108 daemon_controller_ = DaemonController::Create();
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();
119 // Create worker thread for encryption key generation and loading the paired
121 worker_thread_ = AutoThread::Create("ChromotingWorkerThread",
122 plugin_task_runner_);
124 pairing_registry_ = CreatePairingRegistry(worker_thread_);
127 HostNPScriptObject::~HostNPScriptObject() {
128 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
130 HostLogHandler::UnregisterLoggingScriptObject(this);
132 // Stop the It2Me host if the caller forgot to.
133 if (it2me_host_.get()) {
134 it2me_host_->Disconnect();
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);
159 bool HostNPScriptObject::InvokeDefault(const NPVariant* args,
162 VLOG(2) << "InvokeDefault";
163 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
164 SetException("exception during default invocation");
168 bool HostNPScriptObject::Invoke(const std::string& method_name,
169 const NPVariant* args,
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);
205 SetException("Invoke: unknown method " + method_name);
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);
234 bool HostNPScriptObject::GetProperty(const std::string& property_name,
236 VLOG(2) << "GetProperty " << property_name;
237 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
239 SetException("GetProperty: NULL result");
243 if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
244 OBJECT_TO_NPVARIANT(on_nat_traversal_policy_changed_func_.get(), *result);
246 } else if (property_name == kAttrNameOnStateChanged) {
247 OBJECT_TO_NPVARIANT(on_state_changed_func_.get(), *result);
249 } else if (property_name == kAttrNameLogDebugInfo) {
250 OBJECT_TO_NPVARIANT(log_debug_info_func_.get(), *result);
252 } else if (property_name == kAttrNameState) {
253 INT32_TO_NPVARIANT(state_, *result);
255 } else if (property_name == kAttrNameAccessCode) {
256 *result = NPVariantFromString(access_code_);
258 } else if (property_name == kAttrNameAccessCodeLifetime) {
259 INT32_TO_NPVARIANT(access_code_lifetime_.InSeconds(), *result);
261 } else if (property_name == kAttrNameClient) {
262 *result = NPVariantFromString(client_username_);
264 } else if (property_name == kAttrNameDaemonState) {
265 INT32_TO_NPVARIANT(daemon_controller_->GetState(), *result);
267 } else if (property_name == kAttrNameDisconnected) {
268 INT32_TO_NPVARIANT(kDisconnected, *result);
270 } else if (property_name == kAttrNameStarting) {
271 INT32_TO_NPVARIANT(kStarting, *result);
273 } else if (property_name == kAttrNameRequestedAccessCode) {
274 INT32_TO_NPVARIANT(kRequestedAccessCode, *result);
276 } else if (property_name == kAttrNameReceivedAccessCode) {
277 INT32_TO_NPVARIANT(kReceivedAccessCode, *result);
279 } else if (property_name == kAttrNameConnected) {
280 INT32_TO_NPVARIANT(kConnected, *result);
282 } else if (property_name == kAttrNameDisconnecting) {
283 INT32_TO_NPVARIANT(kDisconnecting, *result);
285 } else if (property_name == kAttrNameError) {
286 INT32_TO_NPVARIANT(kError, *result);
288 } else if (property_name == kAttrNameInvalidDomainError) {
289 INT32_TO_NPVARIANT(kInvalidDomainError, *result);
291 } else if (property_name == kAttrNameXmppServerAddress) {
292 *result = NPVariantFromString(base::StringPrintf(
293 "%s:%u", xmpp_server_config_.host.c_str(), xmpp_server_config_.port));
295 } else if (property_name == kAttrNameXmppServerUseTls) {
296 BOOLEAN_TO_NPVARIANT(xmpp_server_config_.use_tls, *result);
298 } else if (property_name == kAttrNameDirectoryBotJid) {
299 *result = NPVariantFromString(directory_bot_jid_);
301 } else if (property_name == kAttrNameSupportedFeatures) {
302 *result = NPVariantFromString(kSupportedFeatures);
305 SetException("GetProperty: unsupported property " + property_name);
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());
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();
324 SetException("SetProperty: unexpected type for property " +
330 if (property_name == kAttrNameOnStateChanged) {
331 if (NPVARIANT_IS_OBJECT(*value)) {
332 on_state_changed_func_ = NPVARIANT_TO_OBJECT(*value);
335 SetException("SetProperty: unexpected type for property " +
341 if (property_name == kAttrNameLogDebugInfo) {
342 if (NPVARIANT_IS_OBJECT(*value)) {
343 log_debug_info_func_ = NPVARIANT_TO_OBJECT(*value);
344 HostLogHandler::RegisterLoggingScriptObject(this);
347 SetException("SetProperty: unexpected type for property " +
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) {
362 SetException("SetProperty: invalid value for property " +
366 SetException("SetProperty: unexpected type for property " +
372 if (property_name == kAttrNameXmppServerUseTls) {
373 if (NPVARIANT_IS_BOOLEAN(*value)) {
374 xmpp_server_config_.use_tls = NPVARIANT_TO_BOOLEAN(*value);
377 SetException("SetProperty: unexpected type for property " +
383 if (property_name == kAttrNameDirectoryBotJid) {
384 if (NPVARIANT_IS_STRING(*value)) {
385 directory_bot_jid_ = StringFromNPVariant(*value);
388 SetException("SetProperty: unexpected type for property " +
393 #endif // !defined(NDEBUG)
398 bool HostNPScriptObject::RemoveProperty(const std::string& property_name) {
399 VLOG(2) << "RemoveProperty " << property_name;
400 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
404 bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) {
405 VLOG(2) << "Enumerate";
406 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
407 const char* entries[] = {
410 kAttrNameLogDebugInfo,
411 kAttrNameOnStateChanged,
412 kAttrNameDisconnected,
414 kAttrNameRequestedAccessCode,
415 kAttrNameReceivedAccessCode,
417 kAttrNameDisconnecting,
419 kAttrNameXmppServerAddress,
420 kAttrNameXmppServerUseTls,
421 kAttrNameDirectoryBotJid,
425 kFuncNameClearPairedClients,
426 kFuncNameDeletePairedClient,
427 kFuncNameGetHostName,
429 kFuncNameGenerateKeyPair,
430 kFuncNameUpdateDaemonConfig,
431 kFuncNameGetDaemonConfig,
432 kFuncNameGetDaemonVersion,
433 kFuncNameGetPairedClients,
434 kFuncNameGetUsageStatsConsent,
435 kFuncNameStartDaemon,
438 for (size_t i = 0; i < arraysize(entries); ++i) {
439 values->push_back(entries[i]);
444 // string username, string auth_token
445 bool HostNPScriptObject::Connect(const NPVariant* args,
448 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
450 LOG(INFO) << "Connecting...";
452 if (arg_count != 2) {
453 SetException("connect: bad number of arguments");
457 if (it2me_host_.get()) {
458 SetException("connect: can be called only when disconnected");
462 XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_;
464 xmpp_config.username = StringFromNPVariant(args[0]);
465 if (xmpp_config.username.empty()) {
466 SetException("connect: bad username argument");
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");
478 // Create threads for the Chromoting host & desktop environment to use.
479 scoped_ptr<ChromotingHostContext> host_context =
480 ChromotingHostContext::Create(plugin_task_runner_);
482 SetException("connect: failed to start threads");
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();
495 bool HostNPScriptObject::Disconnect(const NPVariant* args,
498 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
499 if (arg_count != 0) {
500 SetException("disconnect: bad number of arguments");
504 if (it2me_host_.get()) {
505 it2me_host_->Disconnect();
512 bool HostNPScriptObject::Localize(const NPVariant* args,
515 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
516 if (arg_count != 1) {
517 SetException("localize: bad number of arguments");
521 if (NPVARIANT_IS_OBJECT(args[0])) {
522 ScopedRefNPObject localize_func(NPVARIANT_TO_OBJECT(args[0]));
523 LocalizeStrings(localize_func.get());
526 SetException("localize: unexpected type for argument 1");
531 bool HostNPScriptObject::ClearPairedClients(const NPVariant* args,
534 if (arg_count != 1) {
535 SetException("clearPairedClients: bad number of arguments");
539 if (!NPVARIANT_IS_OBJECT(args[0])) {
540 SetException("clearPairedClients: invalid callback parameter");
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)));
551 InvokeBooleanCallback(callback_obj.Pass(), false);
557 bool HostNPScriptObject::DeletePairedClient(const NPVariant* args,
560 if (arg_count != 2) {
561 SetException("deletePairedClient: bad number of arguments");
565 if (!NPVARIANT_IS_STRING(args[0])) {
566 SetException("deletePairedClient: bad clientId parameter");
570 if (!NPVARIANT_IS_OBJECT(args[1])) {
571 SetException("deletePairedClient: invalid callback parameter");
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(
581 base::Bind(&HostNPScriptObject::InvokeBooleanCallback,
582 weak_ptr_, base::Passed(&callback_obj)));
584 InvokeBooleanCallback(callback_obj.Pass(), false);
590 bool HostNPScriptObject::GetHostName(const NPVariant* args,
593 if (arg_count != 1) {
594 SetException("getHostName: bad number of arguments");
598 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0]));
599 if (!callback_obj.get()) {
600 SetException("getHostName: invalid callback parameter");
604 NPVariant host_name_val = NPVariantFromString(net::GetHostName());
605 InvokeAndIgnoreResult(callback_obj, &host_name_val, 1);
606 g_npnetscape_funcs->releasevariantvalue(&host_name_val);
611 bool HostNPScriptObject::GetPinHash(const NPVariant* args,
614 if (arg_count != 3) {
615 SetException("getPinHash: bad number of arguments");
619 std::string host_id = StringFromNPVariant(args[0]);
620 if (host_id.empty()) {
621 SetException("getPinHash: bad hostId parameter");
625 if (!NPVARIANT_IS_STRING(args[1])) {
626 SetException("getPinHash: bad pin parameter");
629 std::string pin = StringFromNPVariant(args[1]);
631 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[2]));
632 if (!callback_obj.get()) {
633 SetException("getPinHash: invalid callback parameter");
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);
645 bool HostNPScriptObject::GenerateKeyPair(const NPVariant* args,
648 if (arg_count != 1) {
649 SetException("generateKeyPair: bad number of arguments");
653 scoped_ptr<ScopedRefNPObject> callback_obj(
654 new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
655 if (!callback_obj->get()) {
656 SetException("generateKeyPair: invalid callback parameter");
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));
670 bool HostNPScriptObject::UpdateDaemonConfig(const NPVariant* args,
673 if (arg_count != 2) {
674 SetException("updateDaemonConfig: bad number of arguments");
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");
686 scoped_ptr<base::DictionaryValue> config_dict(
687 reinterpret_cast<base::DictionaryValue*>(config.release()));
689 scoped_ptr<ScopedRefNPObject> callback_obj(
690 new ScopedRefNPObject(ObjectFromNPVariant(args[1])));
691 if (!callback_obj->get()) {
692 SetException("updateDaemonConfig: invalid callback parameter");
696 if (config_dict->HasKey(kHostIdConfigPath) ||
697 config_dict->HasKey(kXmppLoginConfigPath)) {
698 SetException("updateDaemonConfig: trying to update immutable config "
703 daemon_controller_->UpdateConfig(
705 base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
706 base::Passed(&callback_obj)));
710 bool HostNPScriptObject::GetDaemonConfig(const NPVariant* args,
713 if (arg_count != 1) {
714 SetException("getDaemonConfig: bad number of arguments");
718 scoped_ptr<ScopedRefNPObject> callback_obj(
719 new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
720 if (!callback_obj->get()) {
721 SetException("getDaemonConfig: invalid callback parameter");
725 daemon_controller_->GetConfig(
726 base::Bind(&HostNPScriptObject::InvokeGetDaemonConfigCallback, weak_ptr_,
727 base::Passed(&callback_obj)));
731 bool HostNPScriptObject::GetDaemonVersion(const NPVariant* args,
734 if (arg_count != 1) {
735 SetException("getDaemonVersion: bad number of arguments");
739 scoped_ptr<ScopedRefNPObject> callback_obj(
740 new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
741 if (!callback_obj->get()) {
742 SetException("getDaemonVersion: invalid callback parameter");
746 daemon_controller_->GetVersion(
747 base::Bind(&HostNPScriptObject::InvokeGetDaemonVersionCallback, weak_ptr_,
748 base::Passed(&callback_obj)));
753 bool HostNPScriptObject::GetPairedClients(const NPVariant* args,
756 if (arg_count != 1) {
757 SetException("getPairedClients: bad number of arguments");
761 scoped_ptr<ScopedRefNPObject> callback_obj(
762 new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
763 if (!callback_obj->get()) {
764 SetException("getPairedClients: invalid callback parameter");
768 if (pairing_registry_) {
769 pairing_registry_->GetAllPairings(
770 base::Bind(&HostNPScriptObject::InvokeGetPairedClientsCallback,
771 weak_ptr_, base::Passed(&callback_obj)));
773 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
774 InvokeGetPairedClientsCallback(callback_obj.Pass(),
775 no_paired_clients.Pass());
780 bool HostNPScriptObject::GetUsageStatsConsent(const NPVariant* args,
783 if (arg_count != 1) {
784 SetException("getUsageStatsConsent: bad number of arguments");
788 scoped_ptr<ScopedRefNPObject> callback_obj(
789 new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
790 if (!callback_obj->get()) {
791 SetException("getUsageStatsConsent: invalid callback parameter");
795 daemon_controller_->GetUsageStatsConsent(
796 base::Bind(&HostNPScriptObject::InvokeGetUsageStatsConsentCallback,
797 weak_ptr_, base::Passed(&callback_obj)));
801 bool HostNPScriptObject::StartDaemon(const NPVariant* args,
804 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
806 if (arg_count != 3) {
807 SetException("startDaemon: bad number of arguments");
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");
819 scoped_ptr<base::DictionaryValue> config_dict(
820 reinterpret_cast<base::DictionaryValue*>(config.release()));
822 if (!NPVARIANT_IS_BOOLEAN(args[1])) {
823 SetException("startDaemon: invalid consent parameter");
827 scoped_ptr<ScopedRefNPObject> callback_obj(
828 new ScopedRefNPObject(ObjectFromNPVariant(args[2])));
829 if (!callback_obj->get()) {
830 SetException("startDaemon: invalid callback parameter");
834 daemon_controller_->SetConfigAndStart(
836 NPVARIANT_TO_BOOLEAN(args[1]),
837 base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
838 base::Passed(&callback_obj)));
842 bool HostNPScriptObject::StopDaemon(const NPVariant* args,
845 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
847 if (arg_count != 1) {
848 SetException("stopDaemon: bad number of arguments");
852 scoped_ptr<ScopedRefNPObject> callback_obj(
853 new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
854 if (!callback_obj->get()) {
855 SetException("stopDaemon: invalid callback parameter");
859 daemon_controller_->Stop(
860 base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
861 base::Passed(&callback_obj)));
865 void HostNPScriptObject::OnStateChanged(It2MeHostState state) {
866 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
870 if (state_ == kDisconnected)
871 client_username_.clear();
873 if (on_state_changed_func_.get()) {
875 INT32_TO_NPVARIANT(state, state_var);
876 InvokeAndIgnoreResult(on_state_changed_func_, &state_var, 1);
880 void HostNPScriptObject::OnNatPolicyChanged(bool nat_traversal_enabled) {
881 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
883 if (on_nat_traversal_policy_changed_func_.get()) {
885 BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy);
886 InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_,
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());
896 access_code_ = access_code;
897 access_code_lifetime_ = access_code_lifetime;
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());
905 client_username_ = client_username;
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_)
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));
923 void HostNPScriptObject::SetWindow(NPWindow* np_window) {
924 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
926 daemon_controller_->SetWindow(np_window->window);
929 void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) {
930 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
932 // Reload resources for the current locale. The default UI locale is used on
936 LocalizeString(localize_func, "@@ui_locale", &ui_locale);
937 remoting::LoadResources(UTF16ToUTF8(ui_locale));
938 #endif // !defined(OS_WIN)
941 bool HostNPScriptObject::LocalizeString(NPObject* localize_func,
942 const char* tag, string16* result) {
943 return LocalizeStringWithSubstitution(localize_func, tag, NULL, result);
946 bool HostNPScriptObject::LocalizeStringWithSubstitution(
947 NPObject* localize_func,
949 const char* substitution,
951 int argc = substitution ? 2 : 1;
952 scoped_ptr<NPVariant[]> args(new NPVariant[argc]);
953 STRINGZ_TO_NPVARIANT(tag, args[0]);
955 STRINGZ_TO_NPVARIANT(substitution, args[1]);
958 bool is_good = g_npnetscape_funcs->invokeDefault(
959 plugin_, localize_func, args.get(), argc, &np_result);
961 LOG(ERROR) << "Localization failed for " << tag;
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;
970 *result = UTF8ToUTF16(translation);
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()));
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());
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]));
999 void HostNPScriptObject::InvokeAsyncResultCallback(
1000 scoped_ptr<ScopedRefNPObject> callback,
1001 DaemonController::AsyncResult result) {
1002 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
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);
1010 void HostNPScriptObject::InvokeBooleanCallback(
1011 scoped_ptr<ScopedRefNPObject> callback, bool result) {
1012 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1014 NPVariant result_var;
1015 BOOLEAN_TO_NPVARIANT(result, result_var);
1016 InvokeAndIgnoreResult(*callback, &result_var, 1);
1017 g_npnetscape_funcs->releasevariantvalue(&result_var);
1020 void HostNPScriptObject::InvokeGetDaemonConfigCallback(
1021 scoped_ptr<ScopedRefNPObject> callback,
1022 scoped_ptr<base::DictionaryValue> config) {
1023 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
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;
1029 base::JSONWriter::Write(config.get(), &config_str);
1031 NPVariant config_val = NPVariantFromString(config_str);
1032 InvokeAndIgnoreResult(*callback, &config_val, 1);
1033 g_npnetscape_funcs->releasevariantvalue(&config_val);
1036 void HostNPScriptObject::InvokeGetDaemonVersionCallback(
1037 scoped_ptr<ScopedRefNPObject> callback, const std::string& version) {
1038 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1040 NPVariant version_val = NPVariantFromString(version);
1041 InvokeAndIgnoreResult(*callback, &version_val, 1);
1042 g_npnetscape_funcs->releasevariantvalue(&version_val);
1045 void HostNPScriptObject::InvokeGetPairedClientsCallback(
1046 scoped_ptr<ScopedRefNPObject> callback,
1047 scoped_ptr<base::ListValue> paired_clients) {
1048 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1050 std::string paired_clients_json;
1051 base::JSONWriter::Write(paired_clients.get(), &paired_clients_json);
1053 NPVariant paired_clients_val = NPVariantFromString(paired_clients_json);
1054 InvokeAndIgnoreResult(*callback, &paired_clients_val, 1);
1055 g_npnetscape_funcs->releasevariantvalue(&paired_clients_val);
1058 void HostNPScriptObject::InvokeGetUsageStatsConsentCallback(
1059 scoped_ptr<ScopedRefNPObject> callback,
1060 const DaemonController::UsageStatsConsent& consent) {
1061 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
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]));
1073 void HostNPScriptObject::LogDebugInfo(const std::string& message) {
1074 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
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_,
1083 LOG(ERROR) << "ERROR - LogDebugInfo failed\n";
1085 am_currently_logging_ = false;
1089 bool HostNPScriptObject::InvokeAndIgnoreResult(const ScopedRefNPObject& func,
1090 const NPVariant* args,
1091 uint32_t arg_count) {
1092 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1094 NPVariant np_result;
1095 bool is_good = g_npnetscape_funcs->invokeDefault(plugin_, func.get(), args,
1096 arg_count, &np_result);
1098 g_npnetscape_funcs->releasevariantvalue(&np_result);
1103 void HostNPScriptObject::SetException(const std::string& exception_string) {
1104 DCHECK(plugin_task_runner_->BelongsToCurrentThread());
1106 g_npnetscape_funcs->setexception(parent_, exception_string.c_str());
1107 LOG(INFO) << exception_string;
1110 } // namespace remoting