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/message_loop/message_loop.h" 11 #include "base/message_loop/message_loop_proxy.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/sys_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/threading/platform_thread.h" 17 #include "base/values.h" 18 #include "net/base/net_util.h" 19 #include "remoting/base/auth_token_util.h" 20 #include "remoting/base/auto_thread.h" 21 #include "remoting/base/resources.h" 22 #include "remoting/base/rsa_key_pair.h" 23 #include "remoting/host/chromoting_host.h" 24 #include "remoting/host/chromoting_host_context.h" 25 #include "remoting/host/host_config.h" 26 #include "remoting/host/host_event_logger.h" 27 #include "remoting/host/host_secret.h" 28 #include "remoting/host/host_status_observer.h" 29 #include "remoting/host/it2me_desktop_environment.h" 30 #include "remoting/host/pairing_registry_delegate.h" 31 #include "remoting/host/pin_hash.h" 32 #include "remoting/host/plugin/host_log_handler.h" 33 #include "remoting/host/policy_hack/policy_watcher.h" 34 #include "remoting/host/register_support_host_request.h" 35 #include "remoting/host/service_urls.h" 36 #include "remoting/host/session_manager_factory.h" 37 #include "remoting/jingle_glue/network_settings.h" 38 #include "remoting/jingle_glue/xmpp_signal_strategy.h" 39 #include "remoting/protocol/it2me_host_authenticator_factory.h" 40 #include "third_party/npapi/bindings/npruntime.h" 41 42 namespace remoting { 43 44 namespace { 45 46 // This is used for tagging system event logs. 47 const char kApplicationName[] = "chromoting"; 48 49 const char* kAttrNameAccessCode = "accessCode"; 50 const char* kAttrNameAccessCodeLifetime = "accessCodeLifetime"; 51 const char* kAttrNameClient = "client"; 52 const char* kAttrNameDaemonState = "daemonState"; 53 const char* kAttrNameState = "state"; 54 const char* kAttrNameLogDebugInfo = "logDebugInfo"; 55 const char* kAttrNameOnNatTraversalPolicyChanged = 56 "onNatTraversalPolicyChanged"; 57 const char* kAttrNameOnStateChanged = "onStateChanged"; 58 const char* kAttrNameXmppServerAddress = "xmppServerAddress"; 59 const char* kAttrNameXmppServerUseTls = "xmppServerUseTls"; 60 const char* kAttrNameDirectoryBotJid = "directoryBotJid"; 61 const char* kAttrNameSupportedFeatures = "supportedFeatures"; 62 const char* kFuncNameConnect = "connect"; 63 const char* kFuncNameDisconnect = "disconnect"; 64 const char* kFuncNameLocalize = "localize"; 65 const char* kFuncNameClearPairedClients = "clearPairedClients"; 66 const char* kFuncNameDeletePairedClient = "deletePairedClient"; 67 const char* kFuncNameGetHostName = "getHostName"; 68 const char* kFuncNameGetPinHash = "getPinHash"; 69 const char* kFuncNameGenerateKeyPair = "generateKeyPair"; 70 const char* kFuncNameUpdateDaemonConfig = "updateDaemonConfig"; 71 const char* kFuncNameGetDaemonConfig = "getDaemonConfig"; 72 const char* kFuncNameGetDaemonVersion = "getDaemonVersion"; 73 const char* kFuncNameGetPairedClients = "getPairedClients"; 74 const char* kFuncNameGetUsageStatsConsent = "getUsageStatsConsent"; 75 const char* kFuncNameStartDaemon = "startDaemon"; 76 const char* kFuncNameStopDaemon = "stopDaemon"; 77 78 // States. 79 const char* kAttrNameDisconnected = "DISCONNECTED"; 80 const char* kAttrNameStarting = "STARTING"; 81 const char* kAttrNameRequestedAccessCode = "REQUESTED_ACCESS_CODE"; 82 const char* kAttrNameReceivedAccessCode = "RECEIVED_ACCESS_CODE"; 83 const char* kAttrNameConnected = "CONNECTED"; 84 const char* kAttrNameDisconnecting = "DISCONNECTING"; 85 const char* kAttrNameError = "ERROR"; 86 const char* kAttrNameInvalidDomainError = "INVALID_DOMAIN_ERROR"; 87 88 const int kMaxLoginAttempts = 5; 89 90 // Space separated list of features supported in addition to the base protocol. 91 const char* kSupportedFeatures = "pairingRegistry"; 92 93 } // namespace 94 95 // Internal implementation of the plugin's It2Me host function. 96 class HostNPScriptObject::It2MeImpl 97 : public base::RefCountedThreadSafe<It2MeImpl>, 98 public HostStatusObserver { 99 public: 100 It2MeImpl( 101 scoped_ptr<ChromotingHostContext> context, 102 scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner, 103 base::WeakPtr<HostNPScriptObject> script_object, 104 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config, 105 const std::string& directory_bot_jid); 106 107 // Methods called by the script object, from the plugin thread. 108 109 // Creates It2Me host structures and starts the host. 110 void Connect(const std::string& uid, 111 const std::string& auth_token, 112 const std::string& auth_service); 113 114 // Disconnects the host, ready for tear-down. 115 // Also called internally, from the network thread. 116 void Disconnect(); 117 118 // Request a NAT policy notification. 119 void RequestNatPolicy(); 120 121 // remoting::HostStatusObserver implementation. 122 virtual void OnAccessDenied(const std::string& jid) OVERRIDE; 123 virtual void OnClientAuthenticated(const std::string& jid) OVERRIDE; 124 virtual void OnClientDisconnected(const std::string& jid) OVERRIDE; 125 126 private: 127 friend class base::RefCountedThreadSafe<It2MeImpl>; 128 129 virtual ~It2MeImpl(); 130 131 // Updates state of the host. Can be called only on the network thread. 132 void SetState(State state); 133 134 // Returns true if the host is connected. 135 bool IsConnected() const; 136 137 // Called by Connect() to check for policies and start connection process. 138 void ReadPolicyAndConnect(const std::string& uid, 139 const std::string& auth_token, 140 const std::string& auth_service); 141 142 // Called by ReadPolicyAndConnect once policies have been read. 143 void FinishConnect(const std::string& uid, 144 const std::string& auth_token, 145 const std::string& auth_service); 146 147 // Called when the support host registration completes. 148 void OnReceivedSupportID(bool success, 149 const std::string& support_id, 150 const base::TimeDelta& lifetime); 151 152 // Shuts down |host_| on the network thread and posts ShutdownOnUiThread() 153 // to shut down UI thread resources. 154 void ShutdownOnNetworkThread(); 155 156 // Shuts down |desktop_environment_factory_| and |policy_watcher_| on 157 // the UI thread. 158 void ShutdownOnUiThread(); 159 160 // Called when initial policies are read, and when they change. 161 void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies); 162 163 // Handlers for NAT traversal and host domain policies. 164 void UpdateNatPolicy(bool nat_traversal_enabled); 165 void UpdateHostDomainPolicy(const std::string& host_domain); 166 167 // Caller supplied fields. 168 scoped_ptr<ChromotingHostContext> host_context_; 169 scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner_; 170 base::WeakPtr<HostNPScriptObject> script_object_; 171 XmppSignalStrategy::XmppServerConfig xmpp_server_config_; 172 std::string directory_bot_jid_; 173 174 State state_; 175 176 scoped_refptr<RsaKeyPair> host_key_pair_; 177 scoped_ptr<SignalStrategy> signal_strategy_; 178 scoped_ptr<RegisterSupportHostRequest> register_request_; 179 scoped_ptr<LogToServer> log_to_server_; 180 scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_; 181 scoped_ptr<HostEventLogger> host_event_logger_; 182 183 scoped_ptr<ChromotingHost> host_; 184 int failed_login_attempts_; 185 186 scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_; 187 188 // Host the current nat traversal policy setting. 189 bool nat_traversal_enabled_; 190 191 // The host domain policy setting. 192 std::string required_host_domain_; 193 194 // Indicates whether or not a policy has ever been read. This is to ensure 195 // that on startup, we do not accidentally start a connection before we have 196 // queried our policy restrictions. 197 bool policy_received_; 198 199 // On startup, it is possible to have Connect() called before the policy read 200 // is completed. Rather than just failing, we thunk the connection call so 201 // it can be executed after at least one successful policy read. This 202 // variable contains the thunk if it is necessary. 203 base::Closure pending_connect_; 204 205 DISALLOW_COPY_AND_ASSIGN(It2MeImpl); 206 }; 207 208 HostNPScriptObject::It2MeImpl::It2MeImpl( 209 scoped_ptr<ChromotingHostContext> host_context, 210 scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner, 211 base::WeakPtr<HostNPScriptObject> script_object, 212 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config, 213 const std::string& directory_bot_jid) 214 : host_context_(host_context.Pass()), 215 plugin_task_runner_(plugin_task_runner), 216 script_object_(script_object), 217 xmpp_server_config_(xmpp_server_config), 218 directory_bot_jid_(directory_bot_jid), 219 state_(kDisconnected), 220 failed_login_attempts_(0), 221 nat_traversal_enabled_(false), 222 policy_received_(false) { 223 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 224 } 225 226 void HostNPScriptObject::It2MeImpl::Connect( 227 const std::string& uid, 228 const std::string& auth_token, 229 const std::string& auth_service) { 230 if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) { 231 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 232 host_context_->ui_task_runner()->PostTask( 233 FROM_HERE, 234 base::Bind(&It2MeImpl::Connect, this, uid, auth_token, auth_service)); 235 return; 236 } 237 238 desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory( 239 host_context_->network_task_runner(), 240 host_context_->input_task_runner(), 241 host_context_->ui_task_runner())); 242 243 // Start monitoring configured policies. 244 policy_watcher_.reset( 245 policy_hack::PolicyWatcher::Create(host_context_->network_task_runner())); 246 policy_watcher_->StartWatching( 247 base::Bind(&It2MeImpl::OnPolicyUpdate, this)); 248 249 // Switch to the network thread to start the actual connection. 250 host_context_->network_task_runner()->PostTask( 251 FROM_HERE, base::Bind( 252 &It2MeImpl::ReadPolicyAndConnect, this, 253 uid, auth_token, auth_service)); 254 } 255 256 void HostNPScriptObject::It2MeImpl::Disconnect() { 257 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { 258 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 259 host_context_->network_task_runner()->PostTask( 260 FROM_HERE, base::Bind(&It2MeImpl::Disconnect, this)); 261 return; 262 } 263 264 switch (state_) { 265 case kDisconnected: 266 ShutdownOnNetworkThread(); 267 return; 268 269 case kStarting: 270 SetState(kDisconnecting); 271 SetState(kDisconnected); 272 ShutdownOnNetworkThread(); 273 return; 274 275 case kDisconnecting: 276 return; 277 278 default: 279 SetState(kDisconnecting); 280 281 if (!host_) { 282 SetState(kDisconnected); 283 ShutdownOnNetworkThread(); 284 return; 285 } 286 287 // Deleting the host destroys SignalStrategy synchronously, but 288 // SignalStrategy::Listener handlers are not allowed to destroy 289 // SignalStrategy, so post task to destroy the host later. 290 host_context_->network_task_runner()->PostTask( 291 FROM_HERE, base::Bind(&It2MeImpl::ShutdownOnNetworkThread, this)); 292 return; 293 } 294 } 295 296 void HostNPScriptObject::It2MeImpl::RequestNatPolicy() { 297 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { 298 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 299 host_context_->network_task_runner()->PostTask( 300 FROM_HERE, base::Bind(&It2MeImpl::RequestNatPolicy, this)); 301 return; 302 } 303 304 if (policy_received_) 305 UpdateNatPolicy(nat_traversal_enabled_); 306 } 307 308 void HostNPScriptObject::It2MeImpl::ReadPolicyAndConnect( 309 const std::string& uid, 310 const std::string& auth_token, 311 const std::string& auth_service) { 312 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 313 314 SetState(kStarting); 315 316 // Only proceed to FinishConnect() if at least one policy update has been 317 // received. 318 if (policy_received_) { 319 FinishConnect(uid, auth_token, auth_service); 320 } else { 321 // Otherwise, create the policy watcher, and thunk the connect. 322 pending_connect_ = 323 base::Bind(&It2MeImpl::FinishConnect, this, 324 uid, auth_token, auth_service); 325 } 326 } 327 328 void HostNPScriptObject::It2MeImpl::FinishConnect( 329 const std::string& uid, 330 const std::string& auth_token, 331 const std::string& auth_service) { 332 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 333 334 if (state_ != kStarting) { 335 // Host has been stopped while we were fetching policy. 336 return; 337 } 338 339 // Check the host domain policy. 340 if (!required_host_domain_.empty() && 341 !EndsWith(uid, std::string("@") + required_host_domain_, false)) { 342 SetState(kInvalidDomainError); 343 return; 344 } 345 346 // Generate a key pair for the Host to use. 347 // TODO(wez): Move this to the worker thread. 348 host_key_pair_ = RsaKeyPair::Generate(); 349 350 // Create XMPP connection. 351 scoped_ptr<SignalStrategy> signal_strategy( 352 new XmppSignalStrategy(host_context_->url_request_context_getter(), 353 uid, auth_token, auth_service, 354 xmpp_server_config_)); 355 356 // Request registration of the host for support. 357 scoped_ptr<RegisterSupportHostRequest> register_request( 358 new RegisterSupportHostRequest( 359 signal_strategy.get(), host_key_pair_, directory_bot_jid_, 360 base::Bind(&It2MeImpl::OnReceivedSupportID, 361 base::Unretained(this)))); 362 363 // Beyond this point nothing can fail, so save the config and request. 364 signal_strategy_ = signal_strategy.Pass(); 365 register_request_ = register_request.Pass(); 366 367 // If NAT traversal is off then limit port range to allow firewall pin-holing. 368 LOG(INFO) << "NAT state: " << nat_traversal_enabled_; 369 NetworkSettings network_settings( 370 nat_traversal_enabled_ ? 371 NetworkSettings::NAT_TRAVERSAL_ENABLED : 372 NetworkSettings::NAT_TRAVERSAL_DISABLED); 373 if (!nat_traversal_enabled_) { 374 network_settings.min_port = NetworkSettings::kDefaultMinPort; 375 network_settings.max_port = NetworkSettings::kDefaultMaxPort; 376 } 377 378 // Create the host. 379 host_.reset(new ChromotingHost( 380 signal_strategy_.get(), 381 desktop_environment_factory_.get(), 382 CreateHostSessionManager(network_settings, 383 host_context_->url_request_context_getter()), 384 host_context_->audio_task_runner(), 385 host_context_->input_task_runner(), 386 host_context_->video_capture_task_runner(), 387 host_context_->video_encode_task_runner(), 388 host_context_->network_task_runner(), 389 host_context_->ui_task_runner())); 390 host_->AddStatusObserver(this); 391 log_to_server_.reset( 392 new LogToServer(host_->AsWeakPtr(), ServerLogEntry::IT2ME, 393 signal_strategy_.get(), directory_bot_jid_)); 394 395 // Disable audio by default. 396 // TODO(sergeyu): Add UI to enable it. 397 scoped_ptr<protocol::CandidateSessionConfig> protocol_config = 398 protocol::CandidateSessionConfig::CreateDefault(); 399 protocol::CandidateSessionConfig::DisableAudioChannel(protocol_config.get()); 400 host_->set_protocol_config(protocol_config.Pass()); 401 402 // Create event logger. 403 host_event_logger_ = 404 HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName); 405 406 // Connect signaling and start the host. 407 signal_strategy_->Connect(); 408 host_->Start(uid); 409 410 SetState(kRequestedAccessCode); 411 return; 412 } 413 414 void HostNPScriptObject::It2MeImpl::ShutdownOnNetworkThread() { 415 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 416 DCHECK(state_ == kDisconnecting || state_ == kDisconnected); 417 418 if (state_ == kDisconnecting) { 419 host_event_logger_.reset(); 420 host_->RemoveStatusObserver(this); 421 host_.reset(); 422 423 register_request_.reset(); 424 log_to_server_.reset(); 425 signal_strategy_.reset(); 426 SetState(kDisconnected); 427 } 428 429 host_context_->ui_task_runner()->PostTask( 430 FROM_HERE, base::Bind(&It2MeImpl::ShutdownOnUiThread, this)); 431 } 432 433 void HostNPScriptObject::It2MeImpl::ShutdownOnUiThread() { 434 DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread()); 435 436 // Destroy the DesktopEnvironmentFactory, to free thread references. 437 desktop_environment_factory_.reset(); 438 439 // Stop listening for policy updates. 440 if (policy_watcher_.get()) { 441 base::WaitableEvent policy_watcher_stopped_(true, false); 442 policy_watcher_->StopWatching(&policy_watcher_stopped_); 443 policy_watcher_stopped_.Wait(); 444 policy_watcher_.reset(); 445 } 446 } 447 448 void HostNPScriptObject::It2MeImpl::OnAccessDenied(const std::string& jid) { 449 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 450 451 ++failed_login_attempts_; 452 if (failed_login_attempts_ == kMaxLoginAttempts) { 453 Disconnect(); 454 } 455 } 456 457 void HostNPScriptObject::It2MeImpl::OnClientAuthenticated( 458 const std::string& jid) { 459 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 460 461 if (state_ == kDisconnecting) { 462 // Ignore the new connection if we are disconnecting. 463 return; 464 } 465 if (state_ == kConnected) { 466 // If we already connected another client then one of the connections may be 467 // an attacker, so both are suspect and we have to reject the second 468 // connection and shutdown the host. 469 host_->RejectAuthenticatingClient(); 470 Disconnect(); 471 return; 472 } 473 474 std::string client_username = jid; 475 size_t pos = client_username.find('/'); 476 if (pos != std::string::npos) 477 client_username.replace(pos, std::string::npos, ""); 478 479 LOG(INFO) << "Client " << client_username << " connected."; 480 481 // Pass the client user name to the script object before changing state. 482 plugin_task_runner_->PostTask( 483 FROM_HERE, base::Bind(&HostNPScriptObject::StoreClientUsername, 484 script_object_, client_username)); 485 486 SetState(kConnected); 487 } 488 489 void HostNPScriptObject::It2MeImpl::OnClientDisconnected( 490 const std::string& jid) { 491 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 492 493 // Pass the client user name to the script object before changing state. 494 plugin_task_runner_->PostTask( 495 FROM_HERE, base::Bind(&HostNPScriptObject::StoreClientUsername, 496 script_object_, std::string())); 497 498 Disconnect(); 499 } 500 501 void HostNPScriptObject::It2MeImpl::OnPolicyUpdate( 502 scoped_ptr<base::DictionaryValue> policies) { 503 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 504 505 bool nat_policy; 506 if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName, 507 &nat_policy)) { 508 UpdateNatPolicy(nat_policy); 509 } 510 std::string host_domain; 511 if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName, 512 &host_domain)) { 513 UpdateHostDomainPolicy(host_domain); 514 } 515 516 policy_received_ = true; 517 518 if (!pending_connect_.is_null()) { 519 pending_connect_.Run(); 520 pending_connect_.Reset(); 521 } 522 } 523 524 void HostNPScriptObject::It2MeImpl::UpdateNatPolicy( 525 bool nat_traversal_enabled) { 526 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 527 528 VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled; 529 530 // When transitioning from enabled to disabled, force disconnect any 531 // existing session. 532 if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) { 533 Disconnect(); 534 } 535 536 nat_traversal_enabled_ = nat_traversal_enabled; 537 538 // Notify the web-app of the policy setting. 539 plugin_task_runner_->PostTask( 540 FROM_HERE, base::Bind(&HostNPScriptObject::NotifyNatPolicyChanged, 541 script_object_, nat_traversal_enabled_)); 542 } 543 544 void HostNPScriptObject::It2MeImpl::UpdateHostDomainPolicy( 545 const std::string& host_domain) { 546 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 547 548 VLOG(2) << "UpdateHostDomainPolicy: " << host_domain; 549 550 // When setting a host domain policy, force disconnect any existing session. 551 if (!host_domain.empty() && IsConnected()) { 552 Disconnect(); 553 } 554 555 required_host_domain_ = host_domain; 556 } 557 558 HostNPScriptObject::It2MeImpl::~It2MeImpl() { 559 // Check that resources that need to be torn down on the UI thread are gone. 560 DCHECK(!desktop_environment_factory_.get()); 561 DCHECK(!policy_watcher_.get()); 562 } 563 564 void HostNPScriptObject::It2MeImpl::SetState(State state) { 565 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 566 567 switch (state_) { 568 case kDisconnected: 569 DCHECK(state == kStarting || 570 state == kError) << state; 571 break; 572 case kStarting: 573 DCHECK(state == kRequestedAccessCode || 574 state == kDisconnecting || 575 state == kError || 576 state == kInvalidDomainError) << state; 577 break; 578 case kRequestedAccessCode: 579 DCHECK(state == kReceivedAccessCode || 580 state == kDisconnecting || 581 state == kError) << state; 582 break; 583 case kReceivedAccessCode: 584 DCHECK(state == kConnected || 585 state == kDisconnecting || 586 state == kError) << state; 587 break; 588 case kConnected: 589 DCHECK(state == kDisconnecting || 590 state == kDisconnected || 591 state == kError) << state; 592 break; 593 case kDisconnecting: 594 DCHECK(state == kDisconnected) << state; 595 break; 596 case kError: 597 DCHECK(state == kDisconnecting) << state; 598 break; 599 case kInvalidDomainError: 600 DCHECK(state == kDisconnecting) << state; 601 break; 602 }; 603 604 state_ = state; 605 606 // Post a state-change notification to the web-app. 607 plugin_task_runner_->PostTask( 608 FROM_HERE, base::Bind(&HostNPScriptObject::NotifyStateChanged, 609 script_object_, state)); 610 } 611 612 bool HostNPScriptObject::It2MeImpl::IsConnected() const { 613 return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode || 614 state_ == kConnected; 615 } 616 617 void HostNPScriptObject::It2MeImpl::OnReceivedSupportID( 618 bool success, 619 const std::string& support_id, 620 const base::TimeDelta& lifetime) { 621 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 622 623 if (!success) { 624 SetState(kError); 625 Disconnect(); 626 return; 627 } 628 629 std::string host_secret = GenerateSupportHostSecret(); 630 std::string access_code = support_id + host_secret; 631 632 std::string local_certificate = host_key_pair_->GenerateCertificate(); 633 if (local_certificate.empty()) { 634 LOG(ERROR) << "Failed to generate host certificate."; 635 SetState(kError); 636 Disconnect(); 637 return; 638 } 639 640 scoped_ptr<protocol::AuthenticatorFactory> factory( 641 new protocol::It2MeHostAuthenticatorFactory( 642 local_certificate, host_key_pair_, access_code)); 643 host_->SetAuthenticatorFactory(factory.Pass()); 644 645 // Pass the Access Code to the script object before changing state. 646 plugin_task_runner_->PostTask( 647 FROM_HERE, base::Bind(&HostNPScriptObject::StoreAccessCode, 648 script_object_, access_code, lifetime)); 649 650 SetState(kReceivedAccessCode); 651 } 652 653 HostNPScriptObject::HostNPScriptObject( 654 NPP plugin, 655 NPObject* parent, 656 scoped_refptr<AutoThreadTaskRunner> plugin_task_runner) 657 : plugin_(plugin), 658 parent_(parent), 659 plugin_task_runner_(plugin_task_runner), 660 am_currently_logging_(false), 661 state_(kDisconnected), 662 daemon_controller_(DaemonController::Create()), 663 weak_factory_(this), 664 weak_ptr_(weak_factory_.GetWeakPtr()) { 665 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 666 667 // Set the thread task runner for the plugin thread so that timers and other 668 // code using |base::ThreadTaskRunnerHandle| could be used on the plugin 669 // thread. 670 // 671 // If component build is used, Chrome and the plugin may end up sharing base 672 // binary. This means that the instance of |base::ThreadTaskRunnerHandle| 673 // created by Chrome for the current thread is shared as well. This routinely 674 // happens in the development setting so the below check for 675 // |!base::ThreadTaskRunnerHandle::IsSet()| is a hack/workaround allowing this 676 // configuration to work. It lets the plugin to access Chrome's message loop 677 // directly via |base::ThreadTaskRunnerHandle|. This is safe as long as both 678 // Chrome and the plugin are built from the same version of the sources. 679 if (!base::ThreadTaskRunnerHandle::IsSet()) { 680 plugin_task_runner_handle_.reset( 681 new base::ThreadTaskRunnerHandle(plugin_task_runner_)); 682 } 683 684 ServiceUrls* service_urls = ServiceUrls::GetInstance(); 685 bool xmpp_server_valid = net::ParseHostAndPort( 686 service_urls->xmpp_server_address(), 687 &xmpp_server_config_.host, &xmpp_server_config_.port); 688 // For the plugin, this is always the default address, which must be valid. 689 DCHECK(xmpp_server_valid); 690 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls(); 691 directory_bot_jid_ = service_urls->directory_bot_jid(); 692 693 // Create worker thread for encryption key generation and loading the paired 694 // clients. 695 worker_thread_ = AutoThread::Create("ChromotingWorkerThread", 696 plugin_task_runner_); 697 698 pairing_registry_ = CreatePairingRegistry(worker_thread_); 699 } 700 701 HostNPScriptObject::~HostNPScriptObject() { 702 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 703 704 HostLogHandler::UnregisterLoggingScriptObject(this); 705 706 // Stop the It2Me host if the caller forgot to. 707 if (it2me_impl_.get()) { 708 it2me_impl_->Disconnect(); 709 it2me_impl_ = NULL; 710 } 711 } 712 713 bool HostNPScriptObject::HasMethod(const std::string& method_name) { 714 VLOG(2) << "HasMethod " << method_name; 715 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 716 return (method_name == kFuncNameConnect || 717 method_name == kFuncNameDisconnect || 718 method_name == kFuncNameLocalize || 719 method_name == kFuncNameClearPairedClients || 720 method_name == kFuncNameDeletePairedClient || 721 method_name == kFuncNameGetHostName || 722 method_name == kFuncNameGetPinHash || 723 method_name == kFuncNameGenerateKeyPair || 724 method_name == kFuncNameUpdateDaemonConfig || 725 method_name == kFuncNameGetDaemonConfig || 726 method_name == kFuncNameGetDaemonVersion || 727 method_name == kFuncNameGetPairedClients || 728 method_name == kFuncNameGetUsageStatsConsent || 729 method_name == kFuncNameStartDaemon || 730 method_name == kFuncNameStopDaemon); 731 } 732 733 bool HostNPScriptObject::InvokeDefault(const NPVariant* args, 734 uint32_t arg_count, 735 NPVariant* result) { 736 VLOG(2) << "InvokeDefault"; 737 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 738 SetException("exception during default invocation"); 739 return false; 740 } 741 742 bool HostNPScriptObject::Invoke(const std::string& method_name, 743 const NPVariant* args, 744 uint32_t arg_count, 745 NPVariant* result) { 746 VLOG(2) << "Invoke " << method_name; 747 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 748 if (method_name == kFuncNameConnect) { 749 return Connect(args, arg_count, result); 750 } else if (method_name == kFuncNameDisconnect) { 751 return Disconnect(args, arg_count, result); 752 } else if (method_name == kFuncNameLocalize) { 753 return Localize(args, arg_count, result); 754 } else if (method_name == kFuncNameClearPairedClients) { 755 return ClearPairedClients(args, arg_count, result); 756 } else if (method_name == kFuncNameDeletePairedClient) { 757 return DeletePairedClient(args, arg_count, result); 758 } else if (method_name == kFuncNameGetHostName) { 759 return GetHostName(args, arg_count, result); 760 } else if (method_name == kFuncNameGetPinHash) { 761 return GetPinHash(args, arg_count, result); 762 } else if (method_name == kFuncNameGenerateKeyPair) { 763 return GenerateKeyPair(args, arg_count, result); 764 } else if (method_name == kFuncNameUpdateDaemonConfig) { 765 return UpdateDaemonConfig(args, arg_count, result); 766 } else if (method_name == kFuncNameGetDaemonConfig) { 767 return GetDaemonConfig(args, arg_count, result); 768 } else if (method_name == kFuncNameGetDaemonVersion) { 769 return GetDaemonVersion(args, arg_count, result); 770 } else if (method_name == kFuncNameGetPairedClients) { 771 return GetPairedClients(args, arg_count, result); 772 } else if (method_name == kFuncNameGetUsageStatsConsent) { 773 return GetUsageStatsConsent(args, arg_count, result); 774 } else if (method_name == kFuncNameStartDaemon) { 775 return StartDaemon(args, arg_count, result); 776 } else if (method_name == kFuncNameStopDaemon) { 777 return StopDaemon(args, arg_count, result); 778 } else { 779 SetException("Invoke: unknown method " + method_name); 780 return false; 781 } 782 } 783 784 bool HostNPScriptObject::HasProperty(const std::string& property_name) { 785 VLOG(2) << "HasProperty " << property_name; 786 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 787 return (property_name == kAttrNameAccessCode || 788 property_name == kAttrNameAccessCodeLifetime || 789 property_name == kAttrNameClient || 790 property_name == kAttrNameDaemonState || 791 property_name == kAttrNameState || 792 property_name == kAttrNameLogDebugInfo || 793 property_name == kAttrNameOnNatTraversalPolicyChanged || 794 property_name == kAttrNameOnStateChanged || 795 property_name == kAttrNameDisconnected || 796 property_name == kAttrNameStarting || 797 property_name == kAttrNameRequestedAccessCode || 798 property_name == kAttrNameReceivedAccessCode || 799 property_name == kAttrNameConnected || 800 property_name == kAttrNameDisconnecting || 801 property_name == kAttrNameError || 802 property_name == kAttrNameXmppServerAddress || 803 property_name == kAttrNameXmppServerUseTls || 804 property_name == kAttrNameDirectoryBotJid || 805 property_name == kAttrNameSupportedFeatures); 806 } 807 808 bool HostNPScriptObject::GetProperty(const std::string& property_name, 809 NPVariant* result) { 810 VLOG(2) << "GetProperty " << property_name; 811 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 812 if (!result) { 813 SetException("GetProperty: NULL result"); 814 return false; 815 } 816 817 if (property_name == kAttrNameOnNatTraversalPolicyChanged) { 818 OBJECT_TO_NPVARIANT(on_nat_traversal_policy_changed_func_.get(), *result); 819 return true; 820 } else if (property_name == kAttrNameOnStateChanged) { 821 OBJECT_TO_NPVARIANT(on_state_changed_func_.get(), *result); 822 return true; 823 } else if (property_name == kAttrNameLogDebugInfo) { 824 OBJECT_TO_NPVARIANT(log_debug_info_func_.get(), *result); 825 return true; 826 } else if (property_name == kAttrNameState) { 827 INT32_TO_NPVARIANT(state_, *result); 828 return true; 829 } else if (property_name == kAttrNameAccessCode) { 830 *result = NPVariantFromString(access_code_); 831 return true; 832 } else if (property_name == kAttrNameAccessCodeLifetime) { 833 INT32_TO_NPVARIANT(access_code_lifetime_.InSeconds(), *result); 834 return true; 835 } else if (property_name == kAttrNameClient) { 836 *result = NPVariantFromString(client_username_); 837 return true; 838 } else if (property_name == kAttrNameDaemonState) { 839 INT32_TO_NPVARIANT(daemon_controller_->GetState(), *result); 840 return true; 841 } else if (property_name == kAttrNameDisconnected) { 842 INT32_TO_NPVARIANT(kDisconnected, *result); 843 return true; 844 } else if (property_name == kAttrNameStarting) { 845 INT32_TO_NPVARIANT(kStarting, *result); 846 return true; 847 } else if (property_name == kAttrNameRequestedAccessCode) { 848 INT32_TO_NPVARIANT(kRequestedAccessCode, *result); 849 return true; 850 } else if (property_name == kAttrNameReceivedAccessCode) { 851 INT32_TO_NPVARIANT(kReceivedAccessCode, *result); 852 return true; 853 } else if (property_name == kAttrNameConnected) { 854 INT32_TO_NPVARIANT(kConnected, *result); 855 return true; 856 } else if (property_name == kAttrNameDisconnecting) { 857 INT32_TO_NPVARIANT(kDisconnecting, *result); 858 return true; 859 } else if (property_name == kAttrNameError) { 860 INT32_TO_NPVARIANT(kError, *result); 861 return true; 862 } else if (property_name == kAttrNameInvalidDomainError) { 863 INT32_TO_NPVARIANT(kInvalidDomainError, *result); 864 return true; 865 } else if (property_name == kAttrNameXmppServerAddress) { 866 *result = NPVariantFromString(base::StringPrintf( 867 "%s:%u", xmpp_server_config_.host.c_str(), xmpp_server_config_.port)); 868 return true; 869 } else if (property_name == kAttrNameXmppServerUseTls) { 870 BOOLEAN_TO_NPVARIANT(xmpp_server_config_.use_tls, *result); 871 return true; 872 } else if (property_name == kAttrNameDirectoryBotJid) { 873 *result = NPVariantFromString(directory_bot_jid_); 874 return true; 875 } else if (property_name == kAttrNameSupportedFeatures) { 876 *result = NPVariantFromString(kSupportedFeatures); 877 return true; 878 } else { 879 SetException("GetProperty: unsupported property " + property_name); 880 return false; 881 } 882 } 883 884 bool HostNPScriptObject::SetProperty(const std::string& property_name, 885 const NPVariant* value) { 886 VLOG(2) << "SetProperty " << property_name; 887 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 888 889 if (property_name == kAttrNameOnNatTraversalPolicyChanged) { 890 if (NPVARIANT_IS_OBJECT(*value)) { 891 on_nat_traversal_policy_changed_func_ = NPVARIANT_TO_OBJECT(*value); 892 if (it2me_impl_.get()) { 893 // Ask the It2Me implementation to notify the web-app of the policy. 894 it2me_impl_->RequestNatPolicy(); 895 } 896 return true; 897 } else { 898 SetException("SetProperty: unexpected type for property " + 899 property_name); 900 } 901 return false; 902 } 903 904 if (property_name == kAttrNameOnStateChanged) { 905 if (NPVARIANT_IS_OBJECT(*value)) { 906 on_state_changed_func_ = NPVARIANT_TO_OBJECT(*value); 907 return true; 908 } else { 909 SetException("SetProperty: unexpected type for property " + 910 property_name); 911 } 912 return false; 913 } 914 915 if (property_name == kAttrNameLogDebugInfo) { 916 if (NPVARIANT_IS_OBJECT(*value)) { 917 log_debug_info_func_ = NPVARIANT_TO_OBJECT(*value); 918 HostLogHandler::RegisterLoggingScriptObject(this); 919 return true; 920 } else { 921 SetException("SetProperty: unexpected type for property " + 922 property_name); 923 } 924 return false; 925 } 926 927 #if !defined(NDEBUG) 928 if (property_name == kAttrNameXmppServerAddress) { 929 if (NPVARIANT_IS_STRING(*value)) { 930 std::string address = StringFromNPVariant(*value); 931 bool xmpp_server_valid = net::ParseHostAndPort( 932 address, &xmpp_server_config_.host, &xmpp_server_config_.port); 933 if (xmpp_server_valid) { 934 return true; 935 } else { 936 SetException("SetProperty: invalid value for property " + 937 property_name); 938 } 939 } else { 940 SetException("SetProperty: unexpected type for property " + 941 property_name); 942 } 943 return false; 944 } 945 946 if (property_name == kAttrNameXmppServerUseTls) { 947 if (NPVARIANT_IS_BOOLEAN(*value)) { 948 xmpp_server_config_.use_tls = NPVARIANT_TO_BOOLEAN(*value); 949 return true; 950 } else { 951 SetException("SetProperty: unexpected type for property " + 952 property_name); 953 } 954 return false; 955 } 956 957 if (property_name == kAttrNameDirectoryBotJid) { 958 if (NPVARIANT_IS_STRING(*value)) { 959 directory_bot_jid_ = StringFromNPVariant(*value); 960 return true; 961 } else { 962 SetException("SetProperty: unexpected type for property " + 963 property_name); 964 } 965 return false; 966 } 967 #endif // !defined(NDEBUG) 968 969 return false; 970 } 971 972 bool HostNPScriptObject::RemoveProperty(const std::string& property_name) { 973 VLOG(2) << "RemoveProperty " << property_name; 974 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 975 return false; 976 } 977 978 bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) { 979 VLOG(2) << "Enumerate"; 980 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 981 const char* entries[] = { 982 kAttrNameAccessCode, 983 kAttrNameState, 984 kAttrNameLogDebugInfo, 985 kAttrNameOnStateChanged, 986 kAttrNameDisconnected, 987 kAttrNameStarting, 988 kAttrNameRequestedAccessCode, 989 kAttrNameReceivedAccessCode, 990 kAttrNameConnected, 991 kAttrNameDisconnecting, 992 kAttrNameError, 993 kAttrNameXmppServerAddress, 994 kAttrNameXmppServerUseTls, 995 kAttrNameDirectoryBotJid, 996 kFuncNameConnect, 997 kFuncNameDisconnect, 998 kFuncNameLocalize, 999 kFuncNameClearPairedClients, 1000 kFuncNameDeletePairedClient, 1001 kFuncNameGetHostName, 1002 kFuncNameGetPinHash, 1003 kFuncNameGenerateKeyPair, 1004 kFuncNameUpdateDaemonConfig, 1005 kFuncNameGetDaemonConfig, 1006 kFuncNameGetDaemonVersion, 1007 kFuncNameGetPairedClients, 1008 kFuncNameGetUsageStatsConsent, 1009 kFuncNameStartDaemon, 1010 kFuncNameStopDaemon 1011 }; 1012 for (size_t i = 0; i < arraysize(entries); ++i) { 1013 values->push_back(entries[i]); 1014 } 1015 return true; 1016 } 1017 1018 // string uid, string auth_token 1019 bool HostNPScriptObject::Connect(const NPVariant* args, 1020 uint32_t arg_count, 1021 NPVariant* result) { 1022 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1023 1024 LOG(INFO) << "Connecting..."; 1025 1026 if (arg_count != 2) { 1027 SetException("connect: bad number of arguments"); 1028 return false; 1029 } 1030 1031 if (it2me_impl_.get()) { 1032 SetException("connect: can be called only when disconnected"); 1033 return false; 1034 } 1035 1036 std::string uid = StringFromNPVariant(args[0]); 1037 if (uid.empty()) { 1038 SetException("connect: bad uid argument"); 1039 return false; 1040 } 1041 1042 std::string auth_service_with_token = StringFromNPVariant(args[1]); 1043 std::string auth_token; 1044 std::string auth_service; 1045 ParseAuthTokenWithService(auth_service_with_token, &auth_token, 1046 &auth_service); 1047 if (auth_token.empty()) { 1048 SetException("connect: auth_service_with_token argument has empty token"); 1049 return false; 1050 } 1051 1052 // Create threads for the Chromoting host & desktop environment to use. 1053 scoped_ptr<ChromotingHostContext> host_context = 1054 ChromotingHostContext::Create(plugin_task_runner_); 1055 if (!host_context) { 1056 SetException("connect: failed to start threads"); 1057 return false; 1058 } 1059 1060 // Create the It2Me host implementation and start connecting. 1061 it2me_impl_ = new It2MeImpl( 1062 host_context.Pass(), plugin_task_runner_, weak_ptr_, 1063 xmpp_server_config_, directory_bot_jid_); 1064 it2me_impl_->Connect(uid, auth_token, auth_service); 1065 1066 return true; 1067 } 1068 1069 bool HostNPScriptObject::Disconnect(const NPVariant* args, 1070 uint32_t arg_count, 1071 NPVariant* result) { 1072 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1073 if (arg_count != 0) { 1074 SetException("disconnect: bad number of arguments"); 1075 return false; 1076 } 1077 1078 if (it2me_impl_.get()) { 1079 it2me_impl_->Disconnect(); 1080 it2me_impl_ = NULL; 1081 } 1082 1083 return true; 1084 } 1085 1086 bool HostNPScriptObject::Localize(const NPVariant* args, 1087 uint32_t arg_count, 1088 NPVariant* result) { 1089 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1090 if (arg_count != 1) { 1091 SetException("localize: bad number of arguments"); 1092 return false; 1093 } 1094 1095 if (NPVARIANT_IS_OBJECT(args[0])) { 1096 ScopedRefNPObject localize_func(NPVARIANT_TO_OBJECT(args[0])); 1097 LocalizeStrings(localize_func.get()); 1098 return true; 1099 } else { 1100 SetException("localize: unexpected type for argument 1"); 1101 return false; 1102 } 1103 } 1104 1105 bool HostNPScriptObject::ClearPairedClients(const NPVariant* args, 1106 uint32_t arg_count, 1107 NPVariant* result) { 1108 if (arg_count != 1) { 1109 SetException("clearPairedClients: bad number of arguments"); 1110 return false; 1111 } 1112 1113 if (!NPVARIANT_IS_OBJECT(args[0])) { 1114 SetException("clearPairedClients: invalid callback parameter"); 1115 return false; 1116 } 1117 1118 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0])); 1119 if (pairing_registry_) { 1120 pairing_registry_->ClearAllPairings( 1121 base::Bind(&HostNPScriptObject::InvokeBooleanCallback, weak_ptr_, 1122 callback_obj)); 1123 } else { 1124 InvokeBooleanCallback(callback_obj, false); 1125 } 1126 1127 return true; 1128 } 1129 1130 bool HostNPScriptObject::DeletePairedClient(const NPVariant* args, 1131 uint32_t arg_count, 1132 NPVariant* result) { 1133 if (arg_count != 2) { 1134 SetException("deletePairedClient: bad number of arguments"); 1135 return false; 1136 } 1137 1138 if (!NPVARIANT_IS_STRING(args[0])) { 1139 SetException("deletePairedClient: bad clientId parameter"); 1140 return false; 1141 } 1142 1143 if (!NPVARIANT_IS_OBJECT(args[1])) { 1144 SetException("deletePairedClient: invalid callback parameter"); 1145 return false; 1146 } 1147 1148 std::string client_id = StringFromNPVariant(args[0]); 1149 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[1])); 1150 if (pairing_registry_) { 1151 pairing_registry_->DeletePairing( 1152 client_id, 1153 base::Bind(&HostNPScriptObject::InvokeBooleanCallback, 1154 weak_ptr_, callback_obj)); 1155 } else { 1156 InvokeBooleanCallback(callback_obj, false); 1157 } 1158 1159 return true; 1160 } 1161 1162 bool HostNPScriptObject::GetHostName(const NPVariant* args, 1163 uint32_t arg_count, 1164 NPVariant* result) { 1165 if (arg_count != 1) { 1166 SetException("getHostName: bad number of arguments"); 1167 return false; 1168 } 1169 1170 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0])); 1171 if (!callback_obj.get()) { 1172 SetException("getHostName: invalid callback parameter"); 1173 return false; 1174 } 1175 1176 NPVariant host_name_val = NPVariantFromString(net::GetHostName()); 1177 InvokeAndIgnoreResult(callback_obj.get(), &host_name_val, 1); 1178 g_npnetscape_funcs->releasevariantvalue(&host_name_val); 1179 1180 return true; 1181 } 1182 1183 bool HostNPScriptObject::GetPinHash(const NPVariant* args, 1184 uint32_t arg_count, 1185 NPVariant* result) { 1186 if (arg_count != 3) { 1187 SetException("getPinHash: bad number of arguments"); 1188 return false; 1189 } 1190 1191 std::string host_id = StringFromNPVariant(args[0]); 1192 if (host_id.empty()) { 1193 SetException("getPinHash: bad hostId parameter"); 1194 return false; 1195 } 1196 1197 if (!NPVARIANT_IS_STRING(args[1])) { 1198 SetException("getPinHash: bad pin parameter"); 1199 return false; 1200 } 1201 std::string pin = StringFromNPVariant(args[1]); 1202 1203 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[2])); 1204 if (!callback_obj.get()) { 1205 SetException("getPinHash: invalid callback parameter"); 1206 return false; 1207 } 1208 1209 NPVariant pin_hash_val = NPVariantFromString( 1210 remoting::MakeHostPinHash(host_id, pin)); 1211 InvokeAndIgnoreResult(callback_obj.get(), &pin_hash_val, 1); 1212 g_npnetscape_funcs->releasevariantvalue(&pin_hash_val); 1213 1214 return true; 1215 } 1216 1217 bool HostNPScriptObject::GenerateKeyPair(const NPVariant* args, 1218 uint32_t arg_count, 1219 NPVariant* result) { 1220 if (arg_count != 1) { 1221 SetException("generateKeyPair: bad number of arguments"); 1222 return false; 1223 } 1224 1225 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0])); 1226 if (!callback_obj.get()) { 1227 SetException("generateKeyPair: invalid callback parameter"); 1228 return false; 1229 } 1230 1231 // TODO(wez): HostNPScriptObject needn't be touched on worker 1232 // thread, so make DoGenerateKeyPair static and pass it a callback 1233 // to run (crbug.com/156257). 1234 worker_thread_->PostTask( 1235 FROM_HERE, base::Bind(&HostNPScriptObject::DoGenerateKeyPair, 1236 base::Unretained(this), callback_obj)); 1237 return true; 1238 } 1239 1240 bool HostNPScriptObject::UpdateDaemonConfig(const NPVariant* args, 1241 uint32_t arg_count, 1242 NPVariant* result) { 1243 if (arg_count != 2) { 1244 SetException("updateDaemonConfig: bad number of arguments"); 1245 return false; 1246 } 1247 1248 std::string config_str = StringFromNPVariant(args[0]); 1249 scoped_ptr<base::Value> config( 1250 base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS)); 1251 if (config_str.empty() || !config.get() || 1252 !config->IsType(base::Value::TYPE_DICTIONARY)) { 1253 SetException("updateDaemonConfig: bad config parameter"); 1254 return false; 1255 } 1256 scoped_ptr<base::DictionaryValue> config_dict( 1257 reinterpret_cast<base::DictionaryValue*>(config.release())); 1258 1259 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[1])); 1260 if (!callback_obj.get()) { 1261 SetException("updateDaemonConfig: invalid callback parameter"); 1262 return false; 1263 } 1264 1265 if (config_dict->HasKey(kHostIdConfigPath) || 1266 config_dict->HasKey(kXmppLoginConfigPath)) { 1267 SetException("updateDaemonConfig: trying to update immutable config " 1268 "parameters"); 1269 return false; 1270 } 1271 1272 // TODO(wez): Pass a static method here, that will post the result 1273 // back to us on the right thread (crbug.com/156257). 1274 daemon_controller_->UpdateConfig( 1275 config_dict.Pass(), 1276 base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, 1277 base::Unretained(this), callback_obj)); 1278 return true; 1279 } 1280 1281 bool HostNPScriptObject::GetDaemonConfig(const NPVariant* args, 1282 uint32_t arg_count, 1283 NPVariant* result) { 1284 if (arg_count != 1) { 1285 SetException("getDaemonConfig: bad number of arguments"); 1286 return false; 1287 } 1288 1289 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0])); 1290 if (!callback_obj.get()) { 1291 SetException("getDaemonConfig: invalid callback parameter"); 1292 return false; 1293 } 1294 1295 // TODO(wez): Pass a static method here, that will post the result 1296 // back to us on the right thread (crbug.com/156257). 1297 daemon_controller_->GetConfig( 1298 base::Bind(&HostNPScriptObject::InvokeGetDaemonConfigCallback, 1299 base::Unretained(this), callback_obj)); 1300 1301 return true; 1302 } 1303 1304 bool HostNPScriptObject::GetDaemonVersion(const NPVariant* args, 1305 uint32_t arg_count, 1306 NPVariant* result) { 1307 if (arg_count != 1) { 1308 SetException("getDaemonVersion: bad number of arguments"); 1309 return false; 1310 } 1311 1312 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0])); 1313 if (!callback_obj.get()) { 1314 SetException("getDaemonVersion: invalid callback parameter"); 1315 return false; 1316 } 1317 1318 // TODO(wez): Pass a static method here, that will post the result 1319 // back to us on the right thread (crbug.com/156257). 1320 daemon_controller_->GetVersion( 1321 base::Bind(&HostNPScriptObject::InvokeGetDaemonVersionCallback, 1322 base::Unretained(this), callback_obj)); 1323 1324 return true; 1325 } 1326 1327 bool HostNPScriptObject::GetPairedClients(const NPVariant* args, 1328 uint32_t arg_count, 1329 NPVariant* result) { 1330 if (arg_count != 1) { 1331 SetException("getPairedClients: bad number of arguments"); 1332 return false; 1333 } 1334 1335 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0])); 1336 if (!callback_obj.get()) { 1337 SetException("getPairedClients: invalid callback parameter"); 1338 return false; 1339 } 1340 1341 if (pairing_registry_) { 1342 pairing_registry_->GetAllPairings( 1343 base::Bind(&HostNPScriptObject::InvokeGetPairedClientsCallback, 1344 weak_ptr_, callback_obj)); 1345 } else { 1346 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue); 1347 InvokeGetPairedClientsCallback(callback_obj, no_paired_clients.Pass()); 1348 } 1349 return true; 1350 } 1351 1352 bool HostNPScriptObject::GetUsageStatsConsent(const NPVariant* args, 1353 uint32_t arg_count, 1354 NPVariant* result) { 1355 if (arg_count != 1) { 1356 SetException("getUsageStatsConsent: bad number of arguments"); 1357 return false; 1358 } 1359 1360 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0])); 1361 if (!callback_obj.get()) { 1362 SetException("getUsageStatsConsent: invalid callback parameter"); 1363 return false; 1364 } 1365 1366 // TODO(wez): Pass a static method here, that will post the result 1367 // back to us on the right thread (crbug.com/156257). 1368 daemon_controller_->GetUsageStatsConsent( 1369 base::Bind(&HostNPScriptObject::InvokeGetUsageStatsConsentCallback, 1370 base::Unretained(this), callback_obj)); 1371 return true; 1372 } 1373 1374 bool HostNPScriptObject::StartDaemon(const NPVariant* args, 1375 uint32_t arg_count, 1376 NPVariant* result) { 1377 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1378 1379 if (arg_count != 3) { 1380 SetException("startDaemon: bad number of arguments"); 1381 return false; 1382 } 1383 1384 std::string config_str = StringFromNPVariant(args[0]); 1385 scoped_ptr<base::Value> config( 1386 base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS)); 1387 if (config_str.empty() || !config.get() || 1388 !config->IsType(base::Value::TYPE_DICTIONARY)) { 1389 SetException("startDaemon: bad config parameter"); 1390 return false; 1391 } 1392 scoped_ptr<base::DictionaryValue> config_dict( 1393 reinterpret_cast<base::DictionaryValue*>(config.release())); 1394 1395 if (!NPVARIANT_IS_BOOLEAN(args[1])) { 1396 SetException("startDaemon: invalid consent parameter"); 1397 return false; 1398 } 1399 1400 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[2])); 1401 if (!callback_obj.get()) { 1402 SetException("startDaemon: invalid callback parameter"); 1403 return false; 1404 } 1405 1406 // TODO(wez): Pass a static method here, that will post the result 1407 // back to us on the right thread (crbug.com/156257). 1408 daemon_controller_->SetConfigAndStart( 1409 config_dict.Pass(), 1410 NPVARIANT_TO_BOOLEAN(args[1]), 1411 base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, 1412 base::Unretained(this), callback_obj)); 1413 return true; 1414 } 1415 1416 bool HostNPScriptObject::StopDaemon(const NPVariant* args, 1417 uint32_t arg_count, 1418 NPVariant* result) { 1419 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1420 1421 if (arg_count != 1) { 1422 SetException("stopDaemon: bad number of arguments"); 1423 return false; 1424 } 1425 1426 ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0])); 1427 if (!callback_obj.get()) { 1428 SetException("stopDaemon: invalid callback parameter"); 1429 return false; 1430 } 1431 1432 // TODO(wez): Pass a static method here, that will post the result 1433 // back to us on the right thread (crbug.com/156257). 1434 daemon_controller_->Stop( 1435 base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, 1436 base::Unretained(this), callback_obj)); 1437 return true; 1438 } 1439 1440 void HostNPScriptObject::NotifyStateChanged(State state) { 1441 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1442 1443 state_ = state; 1444 1445 if (on_state_changed_func_.get()) { 1446 NPVariant state_var; 1447 INT32_TO_NPVARIANT(state, state_var); 1448 InvokeAndIgnoreResult(on_state_changed_func_.get(), &state_var, 1); 1449 } 1450 } 1451 1452 void HostNPScriptObject::NotifyNatPolicyChanged(bool nat_traversal_enabled) { 1453 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1454 1455 if (on_nat_traversal_policy_changed_func_.get()) { 1456 NPVariant policy; 1457 BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy); 1458 InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_.get(), 1459 &policy, 1); 1460 } 1461 } 1462 1463 // Stores the Access Code for the web-app to query. 1464 void HostNPScriptObject::StoreAccessCode(const std::string& access_code, 1465 base::TimeDelta access_code_lifetime) { 1466 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1467 1468 access_code_ = access_code; 1469 access_code_lifetime_ = access_code_lifetime; 1470 } 1471 1472 // Stores the client user's name for the web-app to query. 1473 void HostNPScriptObject::StoreClientUsername( 1474 const std::string& client_username) { 1475 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1476 1477 client_username_ = client_username; 1478 } 1479 1480 void HostNPScriptObject::PostLogDebugInfo(const std::string& message) { 1481 if (plugin_task_runner_->BelongsToCurrentThread()) { 1482 // Make sure we're not currently processing a log message. 1483 // We only need to check this if we're on the plugin thread. 1484 if (am_currently_logging_) 1485 return; 1486 } 1487 1488 // Always post (even if we're already on the correct thread) so that debug 1489 // log messages are shown in the correct order. 1490 plugin_task_runner_->PostTask( 1491 FROM_HERE, base::Bind(&HostNPScriptObject::LogDebugInfo, 1492 weak_ptr_, message)); 1493 } 1494 1495 void HostNPScriptObject::SetWindow(NPWindow* np_window) { 1496 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1497 1498 daemon_controller_->SetWindow(np_window->window); 1499 } 1500 1501 void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) { 1502 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1503 1504 // Reload resources for the current locale. The default UI locale is used on 1505 // Windows. 1506 #if !defined(OS_WIN) 1507 string16 ui_locale; 1508 LocalizeString(localize_func, "@@ui_locale", &ui_locale); 1509 remoting::LoadResources(UTF16ToUTF8(ui_locale)); 1510 #endif // !defined(OS_WIN) 1511 } 1512 1513 bool HostNPScriptObject::LocalizeString(NPObject* localize_func, 1514 const char* tag, string16* result) { 1515 return LocalizeStringWithSubstitution(localize_func, tag, NULL, result); 1516 } 1517 1518 bool HostNPScriptObject::LocalizeStringWithSubstitution( 1519 NPObject* localize_func, 1520 const char* tag, 1521 const char* substitution, 1522 string16* result) { 1523 int argc = substitution ? 2 : 1; 1524 scoped_ptr<NPVariant[]> args(new NPVariant[argc]); 1525 STRINGZ_TO_NPVARIANT(tag, args[0]); 1526 if (substitution) { 1527 STRINGZ_TO_NPVARIANT(substitution, args[1]); 1528 } 1529 NPVariant np_result; 1530 bool is_good = g_npnetscape_funcs->invokeDefault( 1531 plugin_, localize_func, args.get(), argc, &np_result); 1532 if (!is_good) { 1533 LOG(ERROR) << "Localization failed for " << tag; 1534 return false; 1535 } 1536 std::string translation = StringFromNPVariant(np_result); 1537 g_npnetscape_funcs->releasevariantvalue(&np_result); 1538 if (translation.empty()) { 1539 LOG(ERROR) << "Missing translation for " << tag; 1540 return false; 1541 } 1542 *result = UTF8ToUTF16(translation); 1543 return true; 1544 } 1545 1546 void HostNPScriptObject::DoGenerateKeyPair(const ScopedRefNPObject& callback) { 1547 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate(); 1548 InvokeGenerateKeyPairCallback(callback, key_pair->ToString(), 1549 key_pair->GetPublicKey()); 1550 } 1551 1552 void HostNPScriptObject::InvokeGenerateKeyPairCallback( 1553 const ScopedRefNPObject& callback, 1554 const std::string& private_key, 1555 const std::string& public_key) { 1556 if (!plugin_task_runner_->BelongsToCurrentThread()) { 1557 plugin_task_runner_->PostTask( 1558 FROM_HERE, base::Bind( 1559 &HostNPScriptObject::InvokeGenerateKeyPairCallback, 1560 weak_ptr_, callback, private_key, public_key)); 1561 return; 1562 } 1563 1564 NPVariant params[2]; 1565 params[0] = NPVariantFromString(private_key); 1566 params[1] = NPVariantFromString(public_key); 1567 InvokeAndIgnoreResult(callback.get(), params, arraysize(params)); 1568 g_npnetscape_funcs->releasevariantvalue(&(params[0])); 1569 g_npnetscape_funcs->releasevariantvalue(&(params[1])); 1570 } 1571 1572 void HostNPScriptObject::InvokeAsyncResultCallback( 1573 const ScopedRefNPObject& callback, 1574 DaemonController::AsyncResult result) { 1575 if (!plugin_task_runner_->BelongsToCurrentThread()) { 1576 plugin_task_runner_->PostTask( 1577 FROM_HERE, base::Bind( 1578 &HostNPScriptObject::InvokeAsyncResultCallback, 1579 weak_ptr_, callback, result)); 1580 return; 1581 } 1582 1583 NPVariant result_var; 1584 INT32_TO_NPVARIANT(static_cast<int32>(result), result_var); 1585 InvokeAndIgnoreResult(callback.get(), &result_var, 1); 1586 g_npnetscape_funcs->releasevariantvalue(&result_var); 1587 } 1588 1589 void HostNPScriptObject::InvokeBooleanCallback( 1590 const ScopedRefNPObject& callback, bool result) { 1591 if (!plugin_task_runner_->BelongsToCurrentThread()) { 1592 plugin_task_runner_->PostTask( 1593 FROM_HERE, base::Bind( 1594 &HostNPScriptObject::InvokeBooleanCallback, 1595 weak_ptr_, callback, result)); 1596 return; 1597 } 1598 1599 NPVariant result_var; 1600 BOOLEAN_TO_NPVARIANT(result, result_var); 1601 InvokeAndIgnoreResult(callback.get(), &result_var, 1); 1602 g_npnetscape_funcs->releasevariantvalue(&result_var); 1603 } 1604 1605 void HostNPScriptObject::InvokeGetDaemonConfigCallback( 1606 const ScopedRefNPObject& callback, 1607 scoped_ptr<base::DictionaryValue> config) { 1608 if (!plugin_task_runner_->BelongsToCurrentThread()) { 1609 plugin_task_runner_->PostTask( 1610 FROM_HERE, base::Bind( 1611 &HostNPScriptObject::InvokeGetDaemonConfigCallback, 1612 weak_ptr_, callback, base::Passed(&config))); 1613 return; 1614 } 1615 1616 // There is no easy way to create a dictionary from an NPAPI plugin 1617 // so we have to serialize the dictionary to pass it to JavaScript. 1618 std::string config_str; 1619 if (config.get()) 1620 base::JSONWriter::Write(config.get(), &config_str); 1621 1622 NPVariant config_val = NPVariantFromString(config_str); 1623 InvokeAndIgnoreResult(callback.get(), &config_val, 1); 1624 g_npnetscape_funcs->releasevariantvalue(&config_val); 1625 } 1626 1627 void HostNPScriptObject::InvokeGetDaemonVersionCallback( 1628 const ScopedRefNPObject& callback, const std::string& version) { 1629 if (!plugin_task_runner_->BelongsToCurrentThread()) { 1630 plugin_task_runner_->PostTask( 1631 FROM_HERE, base::Bind( 1632 &HostNPScriptObject::InvokeGetDaemonVersionCallback, 1633 weak_ptr_, callback, version)); 1634 return; 1635 } 1636 1637 NPVariant version_val = NPVariantFromString(version); 1638 InvokeAndIgnoreResult(callback.get(), &version_val, 1); 1639 g_npnetscape_funcs->releasevariantvalue(&version_val); 1640 } 1641 1642 void HostNPScriptObject::InvokeGetPairedClientsCallback( 1643 const ScopedRefNPObject& callback, 1644 scoped_ptr<base::ListValue> paired_clients) { 1645 if (!plugin_task_runner_->BelongsToCurrentThread()) { 1646 plugin_task_runner_->PostTask( 1647 FROM_HERE, base::Bind( 1648 &HostNPScriptObject::InvokeGetPairedClientsCallback, 1649 weak_ptr_, callback, base::Passed(&paired_clients))); 1650 return; 1651 } 1652 1653 std::string paired_clients_json; 1654 base::JSONWriter::Write(paired_clients.get(), &paired_clients_json); 1655 1656 NPVariant paired_clients_val = NPVariantFromString(paired_clients_json); 1657 InvokeAndIgnoreResult(callback.get(), &paired_clients_val, 1); 1658 g_npnetscape_funcs->releasevariantvalue(&paired_clients_val); 1659 } 1660 1661 void HostNPScriptObject::InvokeGetUsageStatsConsentCallback( 1662 const ScopedRefNPObject& callback, 1663 bool supported, 1664 bool allowed, 1665 bool set_by_policy) { 1666 if (!plugin_task_runner_->BelongsToCurrentThread()) { 1667 plugin_task_runner_->PostTask( 1668 FROM_HERE, base::Bind( 1669 &HostNPScriptObject::InvokeGetUsageStatsConsentCallback, 1670 weak_ptr_, callback, supported, allowed, 1671 set_by_policy)); 1672 return; 1673 } 1674 1675 NPVariant params[3]; 1676 BOOLEAN_TO_NPVARIANT(supported, params[0]); 1677 BOOLEAN_TO_NPVARIANT(allowed, params[1]); 1678 BOOLEAN_TO_NPVARIANT(set_by_policy, params[2]); 1679 InvokeAndIgnoreResult(callback.get(), params, arraysize(params)); 1680 g_npnetscape_funcs->releasevariantvalue(&(params[0])); 1681 g_npnetscape_funcs->releasevariantvalue(&(params[1])); 1682 g_npnetscape_funcs->releasevariantvalue(&(params[2])); 1683 } 1684 1685 void HostNPScriptObject::LogDebugInfo(const std::string& message) { 1686 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1687 1688 if (log_debug_info_func_.get()) { 1689 am_currently_logging_ = true; 1690 NPVariant log_message; 1691 STRINGZ_TO_NPVARIANT(message.c_str(), log_message); 1692 bool is_good = InvokeAndIgnoreResult(log_debug_info_func_.get(), 1693 &log_message, 1); 1694 if (!is_good) { 1695 LOG(ERROR) << "ERROR - LogDebugInfo failed\n"; 1696 } 1697 am_currently_logging_ = false; 1698 } 1699 } 1700 1701 bool HostNPScriptObject::InvokeAndIgnoreResult(NPObject* func, 1702 const NPVariant* args, 1703 uint32_t arg_count) { 1704 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1705 1706 NPVariant np_result; 1707 bool is_good = g_npnetscape_funcs->invokeDefault(plugin_, func, args, 1708 arg_count, &np_result); 1709 if (is_good) 1710 g_npnetscape_funcs->releasevariantvalue(&np_result); 1711 1712 return is_good; 1713 } 1714 1715 void HostNPScriptObject::SetException(const std::string& exception_string) { 1716 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1717 1718 g_npnetscape_funcs->setexception(parent_, exception_string.c_str()); 1719 LOG(INFO) << exception_string; 1720 } 1721 1722 } // namespace remoting 1723