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