Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(534)

Unified Diff: remoting/host/plugin/host_script_object.cc

Issue 9617027: Chromoting: Implemented security attention sequence (SAS) emulation on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: CR feedback. Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: remoting/host/plugin/host_script_object.cc
diff --git a/remoting/host/plugin/host_script_object.cc b/remoting/host/plugin/host_script_object.cc
index d25f89cdb5117907534863d57d482c9ac284b499..11b4e7f6e27a356d00320b77bcfd926ba4762436 100644
--- a/remoting/host/plugin/host_script_object.cc
+++ b/remoting/host/plugin/host_script_object.cc
@@ -1,889 +1,890 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "remoting/host/plugin/host_script_object.h"
-#include "remoting/host/plugin/daemon_controller.h"
-
-#include "base/bind.h"
-#include "base/message_loop.h"
-#include "base/message_loop_proxy.h"
-#include "base/sys_string_conversions.h"
-#include "base/threading/platform_thread.h"
-#include "base/utf_string_conversions.h"
-#include "remoting/jingle_glue/xmpp_signal_strategy.h"
-#include "remoting/base/auth_token_util.h"
-#include "remoting/host/chromoting_host.h"
-#include "remoting/host/chromoting_host_context.h"
-#include "remoting/host/desktop_environment.h"
-#include "remoting/host/host_key_pair.h"
-#include "remoting/host/host_secret.h"
-#include "remoting/host/it2me_host_user_interface.h"
-#include "remoting/host/plugin/host_log_handler.h"
-#include "remoting/host/policy_hack/nat_policy.h"
-#include "remoting/host/register_support_host_request.h"
-#include "remoting/protocol/it2me_host_authenticator_factory.h"
-
-namespace remoting {
-
-namespace {
-
-const char* kAttrNameAccessCode = "accessCode";
-const char* kAttrNameAccessCodeLifetime = "accessCodeLifetime";
-const char* kAttrNameClient = "client";
-const char* kAttrNameDaemonState = "daemonState";
-const char* kAttrNameState = "state";
-const char* kAttrNameLogDebugInfo = "logDebugInfo";
-const char* kAttrNameOnNatTraversalPolicyChanged =
- "onNatTraversalPolicyChanged";
-const char* kAttrNameOnStateChanged = "onStateChanged";
-const char* kFuncNameConnect = "connect";
-const char* kFuncNameDisconnect = "disconnect";
-const char* kFuncNameLocalize = "localize";
-const char* kFuncNameSetDaemonPin = "setDaemonPin";
-const char* kFuncNameStartDaemon = "startDaemon";
-const char* kFuncNameStopDaemon = "stopDaemon";
-
-// States.
-const char* kAttrNameDisconnected = "DISCONNECTED";
-const char* kAttrNameStarting = "STARTING";
-const char* kAttrNameRequestedAccessCode = "REQUESTED_ACCESS_CODE";
-const char* kAttrNameReceivedAccessCode = "RECEIVED_ACCESS_CODE";
-const char* kAttrNameConnected = "CONNECTED";
-const char* kAttrNameDisconnecting = "DISCONNECTING";
-const char* kAttrNameError = "ERROR";
-
-const int kMaxLoginAttempts = 5;
-
-} // namespace
-
-HostNPScriptObject::HostNPScriptObject(
- NPP plugin,
- NPObject* parent,
- PluginMessageLoopProxy::Delegate* plugin_thread_delegate)
- : plugin_(plugin),
- parent_(parent),
- state_(kDisconnected),
- np_thread_id_(base::PlatformThread::CurrentId()),
- plugin_message_loop_proxy_(
- new PluginMessageLoopProxy(plugin_thread_delegate)),
- failed_login_attempts_(0),
- daemon_controller_(DaemonController::Create()),
- disconnected_event_(true, false),
- am_currently_logging_(false),
- nat_traversal_enabled_(false),
- policy_received_(false) {
-}
-
-HostNPScriptObject::~HostNPScriptObject() {
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
-
- // Shutdown It2MeHostUserInterface first so that it doesn't try to post
- // tasks on the UI thread while we are stopping the host.
- if (it2me_host_user_interface_.get()) {
- it2me_host_user_interface_->Shutdown();
- }
-
- HostLogHandler::UnregisterLoggingScriptObject(this);
-
- plugin_message_loop_proxy_->Detach();
-
- // Stop listening for policy updates.
- if (nat_policy_.get()) {
- base::WaitableEvent nat_policy_stopped_(true, false);
- nat_policy_->StopWatching(&nat_policy_stopped_);
- nat_policy_stopped_.Wait();
- nat_policy_.reset();
- }
-
- if (host_context_.get()) {
- // Disconnect synchronously. We cannot disconnect asynchronously
- // here because |host_context_| needs to be stopped on the plugin
- // thread, but the plugin thread may not exist after the instance
- // is destroyed.
- disconnected_event_.Reset();
- DisconnectInternal();
- disconnected_event_.Wait();
-
- // Stops all threads.
- host_context_.reset();
- }
-}
-
-bool HostNPScriptObject::Init() {
- DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
- VLOG(2) << "Init";
-
- host_context_.reset(new ChromotingHostContext(plugin_message_loop_proxy_));
- if (!host_context_->Start()) {
- host_context_.reset();
- return false;
- }
-
- nat_policy_.reset(
- policy_hack::NatPolicy::Create(host_context_->network_message_loop()));
- nat_policy_->StartWatching(
- base::Bind(&HostNPScriptObject::OnNatPolicyUpdate,
- base::Unretained(this)));
- return true;
-}
-
-bool HostNPScriptObject::HasMethod(const std::string& method_name) {
- VLOG(2) << "HasMethod " << method_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- return (method_name == kFuncNameConnect ||
- method_name == kFuncNameDisconnect ||
- method_name == kFuncNameLocalize ||
- method_name == kFuncNameSetDaemonPin ||
- method_name == kFuncNameStartDaemon ||
- method_name == kFuncNameStopDaemon);
-}
-
-bool HostNPScriptObject::InvokeDefault(const NPVariant* args,
- uint32_t argCount,
- NPVariant* result) {
- VLOG(2) << "InvokeDefault";
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- SetException("exception during default invocation");
- return false;
-}
-
-bool HostNPScriptObject::Invoke(const std::string& method_name,
- const NPVariant* args,
- uint32_t argCount,
- NPVariant* result) {
- VLOG(2) << "Invoke " << method_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- if (method_name == kFuncNameConnect) {
- return Connect(args, argCount, result);
- } else if (method_name == kFuncNameDisconnect) {
- return Disconnect(args, argCount, result);
- } else if (method_name == kFuncNameLocalize) {
- return Localize(args, argCount, result);
- } else if (method_name == kFuncNameSetDaemonPin) {
- return SetDaemonPin(args, argCount, result);
- } else if (method_name == kFuncNameStartDaemon) {
- return StartDaemon(args, argCount, result);
- } else if (method_name == kFuncNameStopDaemon) {
- return StopDaemon(args, argCount, result);
- } else {
- SetException("Invoke: unknown method " + method_name);
- return false;
- }
-}
-
-bool HostNPScriptObject::HasProperty(const std::string& property_name) {
- VLOG(2) << "HasProperty " << property_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- return (property_name == kAttrNameAccessCode ||
- property_name == kAttrNameAccessCodeLifetime ||
- property_name == kAttrNameClient ||
- property_name == kAttrNameDaemonState ||
- property_name == kAttrNameState ||
- property_name == kAttrNameLogDebugInfo ||
- property_name == kAttrNameOnNatTraversalPolicyChanged ||
- property_name == kAttrNameOnStateChanged ||
- property_name == kAttrNameDisconnected ||
- property_name == kAttrNameStarting ||
- property_name == kAttrNameRequestedAccessCode ||
- property_name == kAttrNameReceivedAccessCode ||
- property_name == kAttrNameConnected ||
- property_name == kAttrNameDisconnecting ||
- property_name == kAttrNameError);
-}
-
-bool HostNPScriptObject::GetProperty(const std::string& property_name,
- NPVariant* result) {
- VLOG(2) << "GetProperty " << property_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- if (!result) {
- SetException("GetProperty: NULL result");
- return false;
- }
-
- if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
- OBJECT_TO_NPVARIANT(on_nat_traversal_policy_changed_func_.get(), *result);
- return true;
- } else if (property_name == kAttrNameOnStateChanged) {
- OBJECT_TO_NPVARIANT(on_state_changed_func_.get(), *result);
- return true;
- } else if (property_name == kAttrNameLogDebugInfo) {
- OBJECT_TO_NPVARIANT(log_debug_info_func_.get(), *result);
- return true;
- } else if (property_name == kAttrNameState) {
- INT32_TO_NPVARIANT(state_, *result);
- return true;
- } else if (property_name == kAttrNameAccessCode) {
- base::AutoLock auto_lock(access_code_lock_);
- *result = NPVariantFromString(access_code_);
- return true;
- } else if (property_name == kAttrNameAccessCodeLifetime) {
- base::AutoLock auto_lock(access_code_lock_);
- INT32_TO_NPVARIANT(access_code_lifetime_.InSeconds(), *result);
- return true;
- } else if (property_name == kAttrNameClient) {
- *result = NPVariantFromString(client_username_);
- return true;
- } else if (property_name == kAttrNameDaemonState) {
- INT32_TO_NPVARIANT(daemon_controller_->GetState(), *result);
- return true;
- } else if (property_name == kAttrNameDisconnected) {
- INT32_TO_NPVARIANT(kDisconnected, *result);
- return true;
- } else if (property_name == kAttrNameStarting) {
- INT32_TO_NPVARIANT(kStarting, *result);
- return true;
- } else if (property_name == kAttrNameRequestedAccessCode) {
- INT32_TO_NPVARIANT(kRequestedAccessCode, *result);
- return true;
- } else if (property_name == kAttrNameReceivedAccessCode) {
- INT32_TO_NPVARIANT(kReceivedAccessCode, *result);
- return true;
- } else if (property_name == kAttrNameConnected) {
- INT32_TO_NPVARIANT(kConnected, *result);
- return true;
- } else if (property_name == kAttrNameDisconnecting) {
- INT32_TO_NPVARIANT(kDisconnecting, *result);
- return true;
- } else if (property_name == kAttrNameError) {
- INT32_TO_NPVARIANT(kError, *result);
- return true;
- } else {
- SetException("GetProperty: unsupported property " + property_name);
- return false;
- }
-}
-
-bool HostNPScriptObject::SetProperty(const std::string& property_name,
- const NPVariant* value) {
- VLOG(2) << "SetProperty " << property_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
-
- if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
- if (NPVARIANT_IS_OBJECT(*value)) {
- on_nat_traversal_policy_changed_func_ = NPVARIANT_TO_OBJECT(*value);
- bool policy_received, nat_traversal_enabled;
- {
- base::AutoLock lock(nat_policy_lock_);
- policy_received = policy_received_;
- nat_traversal_enabled = nat_traversal_enabled_;
- }
- if (policy_received) {
- UpdateWebappNatPolicy(nat_traversal_enabled);
- }
- return true;
- } else {
- SetException("SetProperty: unexpected type for property " +
- property_name);
- }
- return false;
- }
-
- if (property_name == kAttrNameOnStateChanged) {
- if (NPVARIANT_IS_OBJECT(*value)) {
- on_state_changed_func_ = NPVARIANT_TO_OBJECT(*value);
- return true;
- } else {
- SetException("SetProperty: unexpected type for property " +
- property_name);
- }
- return false;
- }
-
- if (property_name == kAttrNameLogDebugInfo) {
- if (NPVARIANT_IS_OBJECT(*value)) {
- log_debug_info_func_ = NPVARIANT_TO_OBJECT(*value);
- HostLogHandler::RegisterLoggingScriptObject(this);
- return true;
- } else {
- SetException("SetProperty: unexpected type for property " +
- property_name);
- }
- return false;
- }
-
- return false;
-}
-
-bool HostNPScriptObject::RemoveProperty(const std::string& property_name) {
- VLOG(2) << "RemoveProperty " << property_name;
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- return false;
-}
-
-bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) {
- VLOG(2) << "Enumerate";
- CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
- const char* entries[] = {
- kAttrNameAccessCode,
- kAttrNameState,
- kAttrNameLogDebugInfo,
- kAttrNameOnStateChanged,
- kAttrNameDisconnected,
- kAttrNameStarting,
- kAttrNameRequestedAccessCode,
- kAttrNameReceivedAccessCode,
- kAttrNameConnected,
- kAttrNameDisconnecting,
- kAttrNameError,
- kFuncNameConnect,
- kFuncNameDisconnect,
- kFuncNameLocalize,
- kFuncNameSetDaemonPin,
- kFuncNameStartDaemon,
- kFuncNameStopDaemon
- };
- for (size_t i = 0; i < arraysize(entries); ++i) {
- values->push_back(entries[i]);
- }
- return true;
-}
-
-void HostNPScriptObject::OnAccessDenied(const std::string& jid) {
- DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
-
- ++failed_login_attempts_;
- if (failed_login_attempts_ == kMaxLoginAttempts) {
- DisconnectInternal();
- }
-}
-
-void HostNPScriptObject::OnClientAuthenticated(const std::string& jid) {
- DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
-
- if (state_ == kDisconnecting) {
- // Ignore the new connection if we are disconnecting.
- return;
- }
-
- client_username_ = jid;
- size_t pos = client_username_.find('/');
- if (pos != std::string::npos)
- client_username_.replace(pos, std::string::npos, "");
- LOG(INFO) << "Client " << client_username_ << " connected.";
- SetState(kConnected);
-}
-
-void HostNPScriptObject::OnClientDisconnected(const std::string& jid) {
- DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
- client_username_.clear();
- DisconnectInternal();
-}
-
-void HostNPScriptObject::OnShutdown() {
- DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
-
- register_request_.reset();
- log_to_server_.reset();
- signal_strategy_.reset();
- host_->RemoveStatusObserver(this);
- host_ = NULL;
-
- if (state_ != kDisconnected) {
- SetState(kDisconnected);
- }
-}
-
-// string uid, string auth_token
-bool HostNPScriptObject::Connect(const NPVariant* args,
- uint32_t arg_count,
- NPVariant* result) {
- DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
-
- LOG(INFO) << "Connecting...";
-
- if (arg_count != 2) {
- SetException("connect: bad number of arguments");
- return false;
- }
-
- if (state_ != kDisconnected) {
- SetException("connect: can be called only when disconnected");
- return false;
- }
-
- std::string uid = StringFromNPVariant(args[0]);
- if (uid.empty()) {
- SetException("connect: bad uid argument");
- return false;
- }
-
- std::string auth_service_with_token = StringFromNPVariant(args[1]);
- std::string auth_token;
- std::string auth_service;
- ParseAuthTokenWithService(auth_service_with_token, &auth_token,
- &auth_service);
- if (auth_token.empty()) {
- SetException("connect: auth_service_with_token argument has empty token");
- return false;
- }
-
- ReadPolicyAndConnect(uid, auth_token, auth_service);
-
- return true;
-}
-
-void HostNPScriptObject::ReadPolicyAndConnect(const std::string& uid,
- const std::string& auth_token,
- const std::string& auth_service) {
- if (!host_context_->network_message_loop()->BelongsToCurrentThread()) {
- host_context_->network_message_loop()->PostTask(
- FROM_HERE, base::Bind(
- &HostNPScriptObject::ReadPolicyAndConnect, base::Unretained(this),
- uid, auth_token, auth_service));
- return;
- }
-
- SetState(kStarting);
-
- // Only proceed to FinishConnect() if at least one policy update has been
- // received.
- if (policy_received_) {
- FinishConnectMainThread(uid, auth_token, auth_service);
- } else {
- // Otherwise, create the policy watcher, and thunk the connect.
- pending_connect_ =
- base::Bind(&HostNPScriptObject::FinishConnectMainThread,
- base::Unretained(this), uid, auth_token, auth_service);
- }
-}
-
-void HostNPScriptObject::FinishConnectMainThread(
- const std::string& uid,
- const std::string& auth_token,
- const std::string& auth_service) {
- if (host_context_->main_message_loop() != MessageLoop::current()) {
- host_context_->main_message_loop()->PostTask(FROM_HERE, base::Bind(
- &HostNPScriptObject::FinishConnectMainThread, base::Unretained(this),
- uid, auth_token, auth_service));
- return;
- }
-
- // DesktopEnvironment must be initialized on the main thread.
- //
- // TODO(sergeyu): Fix DesktopEnvironment so that it can be created
- // on either the UI or the network thread so that we can avoid
- // jumping to the main thread here.
- desktop_environment_.reset(DesktopEnvironment::Create(host_context_.get()));
-
- FinishConnectNetworkThread(uid, auth_token, auth_service);
-}
-
-void HostNPScriptObject::FinishConnectNetworkThread(
- const std::string& uid,
- const std::string& auth_token,
- const std::string& auth_service) {
- if (!host_context_->network_message_loop()->BelongsToCurrentThread()) {
- host_context_->network_message_loop()->PostTask(FROM_HERE, base::Bind(
- &HostNPScriptObject::FinishConnectNetworkThread, base::Unretained(this),
- uid, auth_token, auth_service));
- return;
- }
-
- if (state_ != kStarting) {
- // Host has been stopped while we were fetching policy.
- return;
- }
-
- // Verify that DesktopEnvironment has been created.
- if (desktop_environment_.get() == NULL) {
- SetState(kError);
- return;
- }
-
- // Generate a key pair for the Host to use.
- // TODO(wez): Move this to the worker thread.
- host_key_pair_.Generate();
-
- // Create XMPP connection.
- scoped_ptr<SignalStrategy> signal_strategy(
- new XmppSignalStrategy(host_context_->jingle_thread(), uid,
- auth_token, auth_service));
-
- // Request registration of the host for support.
- scoped_ptr<RegisterSupportHostRequest> register_request(
- new RegisterSupportHostRequest(
- signal_strategy.get(), &host_key_pair_,
- base::Bind(&HostNPScriptObject::OnReceivedSupportID,
- base::Unretained(this))));
-
- // Beyond this point nothing can fail, so save the config and request.
- signal_strategy_.reset(signal_strategy.release());
- register_request_.reset(register_request.release());
-
- // Create the Host.
- LOG(INFO) << "NAT state: " << nat_traversal_enabled_;
- host_ = new ChromotingHost(
- host_context_.get(), signal_strategy_.get(), desktop_environment_.get(),
- protocol::NetworkSettings(nat_traversal_enabled_));
- host_->AddStatusObserver(this);
- log_to_server_.reset(
- new LogToServer(host_, ServerLogEntry::IT2ME, signal_strategy_.get()));
- it2me_host_user_interface_.reset(
- new It2MeHostUserInterface(host_.get(), host_context_.get()));
- it2me_host_user_interface_->Init();
-
- {
- base::AutoLock auto_lock(ui_strings_lock_);
- host_->SetUiStrings(ui_strings_);
- }
-
- signal_strategy_->Connect();
- host_->Start();
-
- SetState(kRequestedAccessCode);
- return;
-}
-
-bool HostNPScriptObject::Disconnect(const NPVariant* args,
- uint32_t arg_count,
- NPVariant* result) {
- DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
- if (arg_count != 0) {
- SetException("disconnect: bad number of arguments");
- return false;
- }
-
- DisconnectInternal();
-
- return true;
-}
-
-bool HostNPScriptObject::Localize(const NPVariant* args,
- uint32_t arg_count,
- NPVariant* result) {
- DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
- if (arg_count != 1) {
- SetException("localize: bad number of arguments");
- return false;
- }
-
- if (NPVARIANT_IS_OBJECT(args[0])) {
- ScopedRefNPObject localize_func(NPVARIANT_TO_OBJECT(args[0]));
- LocalizeStrings(localize_func.get());
- return true;
- } else {
- SetException("localize: unexpected type for argument 1");
- return false;
- }
-}
-
-bool HostNPScriptObject::SetDaemonPin(const NPVariant* args,
- uint32_t arg_count,
- NPVariant* result) {
- if (arg_count != 1) {
- SetException("startDaemon: bad number of arguments");
- return false;
- }
- if (NPVARIANT_IS_STRING(args[0])) {
- bool set_pin_result =
- daemon_controller_->SetPin(StringFromNPVariant(args[0]));
- BOOLEAN_TO_NPVARIANT(set_pin_result, *result);
- return true;
- } else {
- SetException("startDaemon: unexpected type for argument 1");
- return false;
- }
-}
-
-bool HostNPScriptObject::StartDaemon(const NPVariant* args,
- uint32_t arg_count,
- NPVariant* result) {
- if (arg_count != 0) {
- SetException("startDaemon: bad number of arguments");
- return false;
- }
- bool start_result = daemon_controller_->Start();
- BOOLEAN_TO_NPVARIANT(start_result, *result);
- return true;
-}
-
-bool HostNPScriptObject::StopDaemon(const NPVariant* args,
- uint32_t arg_count,
- NPVariant* result) {
- if (arg_count != 0) {
- SetException("startDaemon: bad number of arguments");
- return false;
- }
- bool stop_result = daemon_controller_->Stop();
- BOOLEAN_TO_NPVARIANT(stop_result, *result);
- return true;
-}
-
-void HostNPScriptObject::DisconnectInternal() {
- if (!host_context_->network_message_loop()->BelongsToCurrentThread()) {
- host_context_->network_message_loop()->PostTask(
- FROM_HERE, base::Bind(&HostNPScriptObject::DisconnectInternal,
- base::Unretained(this)));
- return;
- }
-
- switch (state_) {
- case kDisconnected:
- disconnected_event_.Signal();
- return;
-
- case kStarting:
- SetState(kDisconnecting);
- SetState(kDisconnected);
- disconnected_event_.Signal();
- return;
-
- case kDisconnecting:
- return;
-
- default:
- DCHECK(host_);
- SetState(kDisconnecting);
-
- // ChromotingHost::Shutdown() may destroy SignalStrategy
- // synchronously, bug SignalStrategy::Listener handlers are not
- // allowed to destroy SignalStrategy, so post task to call
- // Shutdown() later.
- host_context_->network_message_loop()->PostTask(
- FROM_HERE, base::Bind(
- &ChromotingHost::Shutdown, host_,
- base::Bind(&HostNPScriptObject::OnShutdownFinished,
- base::Unretained(this))));
- }
-}
-
-void HostNPScriptObject::OnShutdownFinished() {
- DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
-
- disconnected_event_.Signal();
-}
-
-void HostNPScriptObject::OnNatPolicyUpdate(bool nat_traversal_enabled) {
- if (!host_context_->network_message_loop()->BelongsToCurrentThread()) {
- host_context_->network_message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&HostNPScriptObject::OnNatPolicyUpdate,
- base::Unretained(this), nat_traversal_enabled));
- return;
- }
-
- VLOG(2) << "OnNatPolicyUpdate: " << nat_traversal_enabled;
-
- // When transitioning from enabled to disabled, force disconnect any
- // existing session.
- if (nat_traversal_enabled_ && !nat_traversal_enabled) {
- DisconnectInternal();
- }
-
- {
- base::AutoLock lock(nat_policy_lock_);
- policy_received_ = true;
- nat_traversal_enabled_ = nat_traversal_enabled;
- }
-
- UpdateWebappNatPolicy(nat_traversal_enabled_);
-
- if (!pending_connect_.is_null()) {
- pending_connect_.Run();
- pending_connect_.Reset();
- }
-}
-
-void HostNPScriptObject::OnReceivedSupportID(
- bool success,
- const std::string& support_id,
- const base::TimeDelta& lifetime) {
- DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
-
- if (!success) {
- SetState(kError);
- DisconnectInternal();
- return;
- }
-
- std::string host_secret = GenerateSupportHostSecret();
- std::string access_code = support_id + host_secret;
- scoped_ptr<protocol::AuthenticatorFactory> factory(
- new protocol::It2MeHostAuthenticatorFactory(
- host_key_pair_.GenerateCertificate(), *host_key_pair_.private_key(),
- access_code));
- host_->SetAuthenticatorFactory(factory.Pass());
-
- {
- base::AutoLock lock(access_code_lock_);
- access_code_ = access_code;
- access_code_lifetime_ = lifetime;
- }
-
- SetState(kReceivedAccessCode);
-}
-
-void HostNPScriptObject::SetState(State state) {
- DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
- switch (state_) {
- case kDisconnected:
- DCHECK(state == kStarting ||
- state == kError) << state;
- break;
- case kStarting:
- DCHECK(state == kRequestedAccessCode ||
- state == kDisconnecting ||
- state == kError) << state;
- break;
- case kRequestedAccessCode:
- DCHECK(state == kReceivedAccessCode ||
- state == kDisconnecting ||
- state == kError) << state;
- break;
- case kReceivedAccessCode:
- DCHECK(state == kConnected ||
- state == kDisconnecting ||
- state == kError) << state;
- break;
- case kConnected:
- DCHECK(state == kDisconnecting ||
- state == kDisconnected ||
- state == kError) << state;
- break;
- case kDisconnecting:
- DCHECK(state == kDisconnected) << state;
- break;
- case kError:
- DCHECK(state == kDisconnecting) << state;
- break;
- };
- state_ = state;
- NotifyStateChanged(state);
-}
-
-void HostNPScriptObject::NotifyStateChanged(State state) {
- if (!plugin_message_loop_proxy_->BelongsToCurrentThread()) {
- plugin_message_loop_proxy_->PostTask(
- FROM_HERE, base::Bind(&HostNPScriptObject::NotifyStateChanged,
- base::Unretained(this), state));
- return;
- }
- if (on_state_changed_func_.get()) {
- VLOG(2) << "Calling state changed " << state;
- NPVariant state_var;
- INT32_TO_NPVARIANT(state, state_var);
- bool is_good = InvokeAndIgnoreResult(on_state_changed_func_.get(),
- &state_var, 1);
- LOG_IF(ERROR, !is_good) << "OnStateChanged failed";
- }
-}
-
-void HostNPScriptObject::PostLogDebugInfo(const std::string& message) {
- if (plugin_message_loop_proxy_->BelongsToCurrentThread()) {
- // Make sure we're not currently processing a log message.
- // We only need to check this if we're on the plugin thread.
- if (am_currently_logging_)
- return;
- }
-
- // Always post (even if we're already on the correct thread) so that debug
- // log messages are shown in the correct order.
- plugin_message_loop_proxy_->PostTask(
- FROM_HERE, base::Bind(&HostNPScriptObject::LogDebugInfo,
- base::Unretained(this), message));
-}
-
-void HostNPScriptObject::LogDebugInfo(const std::string& message) {
- DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
- if (log_debug_info_func_.get()) {
- am_currently_logging_ = true;
- NPVariant log_message;
- STRINGZ_TO_NPVARIANT(message.c_str(), log_message);
- bool is_good = InvokeAndIgnoreResult(log_debug_info_func_.get(),
- &log_message, 1);
- if (!is_good) {
- LOG(ERROR) << "ERROR - LogDebugInfo failed\n";
- }
- am_currently_logging_ = false;
- }
-}
-
-void HostNPScriptObject::SetException(const std::string& exception_string) {
- DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
- g_npnetscape_funcs->setexception(parent_, exception_string.c_str());
- LOG(INFO) << exception_string;
-}
-
-void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) {
- DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
-
- UiStrings ui_strings;
- string16 direction;
- LocalizeString(localize_func, "@@bidi_dir", &direction);
- ui_strings.direction = UTF16ToUTF8(direction) == "rtl" ?
- remoting::UiStrings::RTL : remoting::UiStrings::LTR;
- LocalizeString(localize_func, /*i18n-content*/"PRODUCT_NAME",
- &ui_strings.product_name);
- LocalizeString(localize_func, /*i18n-content*/"DISCONNECT_OTHER_BUTTON",
- &ui_strings.disconnect_button_text);
- LocalizeString(localize_func,
-#if defined(OS_WIN)
- /*i18n-content*/"DISCONNECT_BUTTON_PLUS_SHORTCUT_WINDOWS",
-#elif defined(OS_MACOSX)
- /*i18n-content*/"DISCONNECT_BUTTON_PLUS_SHORTCUT_MAC_OS_X",
-#else
- /*i18n-content*/"DISCONNECT_BUTTON_PLUS_SHORTCUT_LINUX",
-#endif
- &ui_strings.disconnect_button_text_plus_shortcut);
- LocalizeString(localize_func, /*i18n-content*/"CONTINUE_PROMPT",
- &ui_strings.continue_prompt);
- LocalizeString(localize_func, /*i18n-content*/"CONTINUE_BUTTON",
- &ui_strings.continue_button_text);
- LocalizeString(localize_func, /*i18n-content*/"STOP_SHARING_BUTTON",
- &ui_strings.stop_sharing_button_text);
- LocalizeString(localize_func, /*i18n-content*/"MESSAGE_SHARED",
- &ui_strings.disconnect_message);
-
- base::AutoLock auto_lock(ui_strings_lock_);
- ui_strings_ = ui_strings;
-}
-
-bool HostNPScriptObject::LocalizeString(NPObject* localize_func,
- const char* tag, string16* result) {
- NPVariant args[2];
- STRINGZ_TO_NPVARIANT(tag, args[0]);
- NPVariant np_result;
- bool is_good = g_npnetscape_funcs->invokeDefault(
- plugin_, localize_func, &args[0], 1, &np_result);
- if (!is_good) {
- LOG(ERROR) << "Localization failed for " << tag;
- return false;
- }
- std::string translation = StringFromNPVariant(np_result);
- g_npnetscape_funcs->releasevariantvalue(&np_result);
- if (translation.empty()) {
- LOG(ERROR) << "Missing translation for " << tag;
- return false;
- }
- *result = UTF8ToUTF16(translation);
- return true;
-}
-
-void HostNPScriptObject::UpdateWebappNatPolicy(bool nat_traversal_enabled) {
- if (!plugin_message_loop_proxy_->BelongsToCurrentThread()) {
- plugin_message_loop_proxy_->PostTask(
- FROM_HERE, base::Bind(&HostNPScriptObject::UpdateWebappNatPolicy,
- base::Unretained(this), nat_traversal_enabled));
- return;
- }
- if (on_nat_traversal_policy_changed_func_.get()) {
- NPVariant policy;
- BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy);
- InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_.get(),
- &policy, 1);
- }
-}
-
-bool HostNPScriptObject::InvokeAndIgnoreResult(NPObject* func,
- const NPVariant* args,
- uint32_t argCount) {
- NPVariant np_result;
- bool is_good = g_npnetscape_funcs->invokeDefault(plugin_, func, args,
- argCount, &np_result);
- if (is_good)
- g_npnetscape_funcs->releasevariantvalue(&np_result);
- return is_good;
-}
-
-} // namespace remoting
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/plugin/host_script_object.h"
+#include "remoting/host/plugin/daemon_controller.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/sys_string_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "base/utf_string_conversions.h"
+#include "remoting/jingle_glue/xmpp_signal_strategy.h"
+#include "remoting/base/auth_token_util.h"
+#include "remoting/host/chromoting_host.h"
+#include "remoting/host/chromoting_host_context.h"
+#include "remoting/host/desktop_environment.h"
+#include "remoting/host/host_key_pair.h"
+#include "remoting/host/host_secret.h"
+#include "remoting/host/it2me_host_user_interface.h"
+#include "remoting/host/plugin/host_log_handler.h"
+#include "remoting/host/policy_hack/nat_policy.h"
+#include "remoting/host/register_support_host_request.h"
+#include "remoting/protocol/it2me_host_authenticator_factory.h"
+
+namespace remoting {
+
+namespace {
+
+const char* kAttrNameAccessCode = "accessCode";
+const char* kAttrNameAccessCodeLifetime = "accessCodeLifetime";
+const char* kAttrNameClient = "client";
+const char* kAttrNameDaemonState = "daemonState";
+const char* kAttrNameState = "state";
+const char* kAttrNameLogDebugInfo = "logDebugInfo";
+const char* kAttrNameOnNatTraversalPolicyChanged =
+ "onNatTraversalPolicyChanged";
+const char* kAttrNameOnStateChanged = "onStateChanged";
+const char* kFuncNameConnect = "connect";
+const char* kFuncNameDisconnect = "disconnect";
+const char* kFuncNameLocalize = "localize";
+const char* kFuncNameSetDaemonPin = "setDaemonPin";
+const char* kFuncNameStartDaemon = "startDaemon";
+const char* kFuncNameStopDaemon = "stopDaemon";
+
+// States.
+const char* kAttrNameDisconnected = "DISCONNECTED";
+const char* kAttrNameStarting = "STARTING";
+const char* kAttrNameRequestedAccessCode = "REQUESTED_ACCESS_CODE";
+const char* kAttrNameReceivedAccessCode = "RECEIVED_ACCESS_CODE";
+const char* kAttrNameConnected = "CONNECTED";
+const char* kAttrNameDisconnecting = "DISCONNECTING";
+const char* kAttrNameError = "ERROR";
+
+const int kMaxLoginAttempts = 5;
+
+} // namespace
+
+HostNPScriptObject::HostNPScriptObject(
+ NPP plugin,
+ NPObject* parent,
+ PluginMessageLoopProxy::Delegate* plugin_thread_delegate)
+ : plugin_(plugin),
+ parent_(parent),
+ state_(kDisconnected),
+ np_thread_id_(base::PlatformThread::CurrentId()),
+ plugin_message_loop_proxy_(
+ new PluginMessageLoopProxy(plugin_thread_delegate)),
+ failed_login_attempts_(0),
+ daemon_controller_(DaemonController::Create()),
+ disconnected_event_(true, false),
+ am_currently_logging_(false),
+ nat_traversal_enabled_(false),
+ policy_received_(false) {
+}
+
+HostNPScriptObject::~HostNPScriptObject() {
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+
+ // Shutdown It2MeHostUserInterface first so that it doesn't try to post
+ // tasks on the UI thread while we are stopping the host.
+ if (it2me_host_user_interface_.get()) {
+ it2me_host_user_interface_->Shutdown();
+ }
+
+ HostLogHandler::UnregisterLoggingScriptObject(this);
+
+ plugin_message_loop_proxy_->Detach();
+
+ // Stop listening for policy updates.
+ if (nat_policy_.get()) {
+ base::WaitableEvent nat_policy_stopped_(true, false);
+ nat_policy_->StopWatching(&nat_policy_stopped_);
+ nat_policy_stopped_.Wait();
+ nat_policy_.reset();
+ }
+
+ if (host_context_.get()) {
+ // Disconnect synchronously. We cannot disconnect asynchronously
+ // here because |host_context_| needs to be stopped on the plugin
+ // thread, but the plugin thread may not exist after the instance
+ // is destroyed.
+ disconnected_event_.Reset();
+ DisconnectInternal();
+ disconnected_event_.Wait();
+
+ // Stops all threads.
+ host_context_.reset();
+ }
+}
+
+bool HostNPScriptObject::Init() {
+ DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
+ VLOG(2) << "Init";
+
+ host_context_.reset(new ChromotingHostContext(NULL,
+ plugin_message_loop_proxy_));
+ if (!host_context_->Start()) {
+ host_context_.reset();
+ return false;
+ }
+
+ nat_policy_.reset(
+ policy_hack::NatPolicy::Create(host_context_->network_message_loop()));
+ nat_policy_->StartWatching(
+ base::Bind(&HostNPScriptObject::OnNatPolicyUpdate,
+ base::Unretained(this)));
+ return true;
+}
+
+bool HostNPScriptObject::HasMethod(const std::string& method_name) {
+ VLOG(2) << "HasMethod " << method_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ return (method_name == kFuncNameConnect ||
+ method_name == kFuncNameDisconnect ||
+ method_name == kFuncNameLocalize ||
+ method_name == kFuncNameSetDaemonPin ||
+ method_name == kFuncNameStartDaemon ||
+ method_name == kFuncNameStopDaemon);
+}
+
+bool HostNPScriptObject::InvokeDefault(const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result) {
+ VLOG(2) << "InvokeDefault";
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ SetException("exception during default invocation");
+ return false;
+}
+
+bool HostNPScriptObject::Invoke(const std::string& method_name,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result) {
+ VLOG(2) << "Invoke " << method_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ if (method_name == kFuncNameConnect) {
+ return Connect(args, argCount, result);
+ } else if (method_name == kFuncNameDisconnect) {
+ return Disconnect(args, argCount, result);
+ } else if (method_name == kFuncNameLocalize) {
+ return Localize(args, argCount, result);
+ } else if (method_name == kFuncNameSetDaemonPin) {
+ return SetDaemonPin(args, argCount, result);
+ } else if (method_name == kFuncNameStartDaemon) {
+ return StartDaemon(args, argCount, result);
+ } else if (method_name == kFuncNameStopDaemon) {
+ return StopDaemon(args, argCount, result);
+ } else {
+ SetException("Invoke: unknown method " + method_name);
+ return false;
+ }
+}
+
+bool HostNPScriptObject::HasProperty(const std::string& property_name) {
+ VLOG(2) << "HasProperty " << property_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ return (property_name == kAttrNameAccessCode ||
+ property_name == kAttrNameAccessCodeLifetime ||
+ property_name == kAttrNameClient ||
+ property_name == kAttrNameDaemonState ||
+ property_name == kAttrNameState ||
+ property_name == kAttrNameLogDebugInfo ||
+ property_name == kAttrNameOnNatTraversalPolicyChanged ||
+ property_name == kAttrNameOnStateChanged ||
+ property_name == kAttrNameDisconnected ||
+ property_name == kAttrNameStarting ||
+ property_name == kAttrNameRequestedAccessCode ||
+ property_name == kAttrNameReceivedAccessCode ||
+ property_name == kAttrNameConnected ||
+ property_name == kAttrNameDisconnecting ||
+ property_name == kAttrNameError);
+}
+
+bool HostNPScriptObject::GetProperty(const std::string& property_name,
+ NPVariant* result) {
+ VLOG(2) << "GetProperty " << property_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ if (!result) {
+ SetException("GetProperty: NULL result");
+ return false;
+ }
+
+ if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
+ OBJECT_TO_NPVARIANT(on_nat_traversal_policy_changed_func_.get(), *result);
+ return true;
+ } else if (property_name == kAttrNameOnStateChanged) {
+ OBJECT_TO_NPVARIANT(on_state_changed_func_.get(), *result);
+ return true;
+ } else if (property_name == kAttrNameLogDebugInfo) {
+ OBJECT_TO_NPVARIANT(log_debug_info_func_.get(), *result);
+ return true;
+ } else if (property_name == kAttrNameState) {
+ INT32_TO_NPVARIANT(state_, *result);
+ return true;
+ } else if (property_name == kAttrNameAccessCode) {
+ base::AutoLock auto_lock(access_code_lock_);
+ *result = NPVariantFromString(access_code_);
+ return true;
+ } else if (property_name == kAttrNameAccessCodeLifetime) {
+ base::AutoLock auto_lock(access_code_lock_);
+ INT32_TO_NPVARIANT(access_code_lifetime_.InSeconds(), *result);
+ return true;
+ } else if (property_name == kAttrNameClient) {
+ *result = NPVariantFromString(client_username_);
+ return true;
+ } else if (property_name == kAttrNameDaemonState) {
+ INT32_TO_NPVARIANT(daemon_controller_->GetState(), *result);
+ return true;
+ } else if (property_name == kAttrNameDisconnected) {
+ INT32_TO_NPVARIANT(kDisconnected, *result);
+ return true;
+ } else if (property_name == kAttrNameStarting) {
+ INT32_TO_NPVARIANT(kStarting, *result);
+ return true;
+ } else if (property_name == kAttrNameRequestedAccessCode) {
+ INT32_TO_NPVARIANT(kRequestedAccessCode, *result);
+ return true;
+ } else if (property_name == kAttrNameReceivedAccessCode) {
+ INT32_TO_NPVARIANT(kReceivedAccessCode, *result);
+ return true;
+ } else if (property_name == kAttrNameConnected) {
+ INT32_TO_NPVARIANT(kConnected, *result);
+ return true;
+ } else if (property_name == kAttrNameDisconnecting) {
+ INT32_TO_NPVARIANT(kDisconnecting, *result);
+ return true;
+ } else if (property_name == kAttrNameError) {
+ INT32_TO_NPVARIANT(kError, *result);
+ return true;
+ } else {
+ SetException("GetProperty: unsupported property " + property_name);
+ return false;
+ }
+}
+
+bool HostNPScriptObject::SetProperty(const std::string& property_name,
+ const NPVariant* value) {
+ VLOG(2) << "SetProperty " << property_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+
+ if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
+ if (NPVARIANT_IS_OBJECT(*value)) {
+ on_nat_traversal_policy_changed_func_ = NPVARIANT_TO_OBJECT(*value);
+ bool policy_received, nat_traversal_enabled;
+ {
+ base::AutoLock lock(nat_policy_lock_);
+ policy_received = policy_received_;
+ nat_traversal_enabled = nat_traversal_enabled_;
+ }
+ if (policy_received) {
+ UpdateWebappNatPolicy(nat_traversal_enabled);
+ }
+ return true;
+ } else {
+ SetException("SetProperty: unexpected type for property " +
+ property_name);
+ }
+ return false;
+ }
+
+ if (property_name == kAttrNameOnStateChanged) {
+ if (NPVARIANT_IS_OBJECT(*value)) {
+ on_state_changed_func_ = NPVARIANT_TO_OBJECT(*value);
+ return true;
+ } else {
+ SetException("SetProperty: unexpected type for property " +
+ property_name);
+ }
+ return false;
+ }
+
+ if (property_name == kAttrNameLogDebugInfo) {
+ if (NPVARIANT_IS_OBJECT(*value)) {
+ log_debug_info_func_ = NPVARIANT_TO_OBJECT(*value);
+ HostLogHandler::RegisterLoggingScriptObject(this);
+ return true;
+ } else {
+ SetException("SetProperty: unexpected type for property " +
+ property_name);
+ }
+ return false;
+ }
+
+ return false;
+}
+
+bool HostNPScriptObject::RemoveProperty(const std::string& property_name) {
+ VLOG(2) << "RemoveProperty " << property_name;
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ return false;
+}
+
+bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) {
+ VLOG(2) << "Enumerate";
+ CHECK_EQ(base::PlatformThread::CurrentId(), np_thread_id_);
+ const char* entries[] = {
+ kAttrNameAccessCode,
+ kAttrNameState,
+ kAttrNameLogDebugInfo,
+ kAttrNameOnStateChanged,
+ kAttrNameDisconnected,
+ kAttrNameStarting,
+ kAttrNameRequestedAccessCode,
+ kAttrNameReceivedAccessCode,
+ kAttrNameConnected,
+ kAttrNameDisconnecting,
+ kAttrNameError,
+ kFuncNameConnect,
+ kFuncNameDisconnect,
+ kFuncNameLocalize,
+ kFuncNameSetDaemonPin,
+ kFuncNameStartDaemon,
+ kFuncNameStopDaemon
+ };
+ for (size_t i = 0; i < arraysize(entries); ++i) {
+ values->push_back(entries[i]);
+ }
+ return true;
+}
+
+void HostNPScriptObject::OnAccessDenied(const std::string& jid) {
+ DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
+
+ ++failed_login_attempts_;
+ if (failed_login_attempts_ == kMaxLoginAttempts) {
+ DisconnectInternal();
+ }
+}
+
+void HostNPScriptObject::OnClientAuthenticated(const std::string& jid) {
+ DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
+
+ if (state_ == kDisconnecting) {
+ // Ignore the new connection if we are disconnecting.
+ return;
+ }
+
+ client_username_ = jid;
+ size_t pos = client_username_.find('/');
+ if (pos != std::string::npos)
+ client_username_.replace(pos, std::string::npos, "");
+ LOG(INFO) << "Client " << client_username_ << " connected.";
+ SetState(kConnected);
+}
+
+void HostNPScriptObject::OnClientDisconnected(const std::string& jid) {
+ DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
+ client_username_.clear();
+ DisconnectInternal();
+}
+
+void HostNPScriptObject::OnShutdown() {
+ DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
+
+ register_request_.reset();
+ log_to_server_.reset();
+ signal_strategy_.reset();
+ host_->RemoveStatusObserver(this);
+ host_ = NULL;
+
+ if (state_ != kDisconnected) {
+ SetState(kDisconnected);
+ }
+}
+
+// string uid, string auth_token
+bool HostNPScriptObject::Connect(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
+
+ LOG(INFO) << "Connecting...";
+
+ if (arg_count != 2) {
+ SetException("connect: bad number of arguments");
+ return false;
+ }
+
+ if (state_ != kDisconnected) {
+ SetException("connect: can be called only when disconnected");
+ return false;
+ }
+
+ std::string uid = StringFromNPVariant(args[0]);
+ if (uid.empty()) {
+ SetException("connect: bad uid argument");
+ return false;
+ }
+
+ std::string auth_service_with_token = StringFromNPVariant(args[1]);
+ std::string auth_token;
+ std::string auth_service;
+ ParseAuthTokenWithService(auth_service_with_token, &auth_token,
+ &auth_service);
+ if (auth_token.empty()) {
+ SetException("connect: auth_service_with_token argument has empty token");
+ return false;
+ }
+
+ ReadPolicyAndConnect(uid, auth_token, auth_service);
+
+ return true;
+}
+
+void HostNPScriptObject::ReadPolicyAndConnect(const std::string& uid,
+ const std::string& auth_token,
+ const std::string& auth_service) {
+ if (!host_context_->network_message_loop()->BelongsToCurrentThread()) {
+ host_context_->network_message_loop()->PostTask(
+ FROM_HERE, base::Bind(
+ &HostNPScriptObject::ReadPolicyAndConnect, base::Unretained(this),
+ uid, auth_token, auth_service));
+ return;
+ }
+
+ SetState(kStarting);
+
+ // Only proceed to FinishConnect() if at least one policy update has been
+ // received.
+ if (policy_received_) {
+ FinishConnectMainThread(uid, auth_token, auth_service);
+ } else {
+ // Otherwise, create the policy watcher, and thunk the connect.
+ pending_connect_ =
+ base::Bind(&HostNPScriptObject::FinishConnectMainThread,
+ base::Unretained(this), uid, auth_token, auth_service);
+ }
+}
+
+void HostNPScriptObject::FinishConnectMainThread(
+ const std::string& uid,
+ const std::string& auth_token,
+ const std::string& auth_service) {
+ if (host_context_->main_message_loop() != MessageLoop::current()) {
+ host_context_->main_message_loop()->PostTask(FROM_HERE, base::Bind(
+ &HostNPScriptObject::FinishConnectMainThread, base::Unretained(this),
+ uid, auth_token, auth_service));
+ return;
+ }
+
+ // DesktopEnvironment must be initialized on the main thread.
+ //
+ // TODO(sergeyu): Fix DesktopEnvironment so that it can be created
+ // on either the UI or the network thread so that we can avoid
+ // jumping to the main thread here.
+ desktop_environment_ = DesktopEnvironment::Create(host_context_.get());
+
+ FinishConnectNetworkThread(uid, auth_token, auth_service);
+}
+
+void HostNPScriptObject::FinishConnectNetworkThread(
+ const std::string& uid,
+ const std::string& auth_token,
+ const std::string& auth_service) {
+ if (!host_context_->network_message_loop()->BelongsToCurrentThread()) {
+ host_context_->network_message_loop()->PostTask(FROM_HERE, base::Bind(
+ &HostNPScriptObject::FinishConnectNetworkThread, base::Unretained(this),
+ uid, auth_token, auth_service));
+ return;
+ }
+
+ if (state_ != kStarting) {
+ // Host has been stopped while we were fetching policy.
+ return;
+ }
+
+ // Verify that DesktopEnvironment has been created.
+ if (desktop_environment_.get() == NULL) {
+ SetState(kError);
+ return;
+ }
+
+ // Generate a key pair for the Host to use.
+ // TODO(wez): Move this to the worker thread.
+ host_key_pair_.Generate();
+
+ // Create XMPP connection.
+ scoped_ptr<SignalStrategy> signal_strategy(
+ new XmppSignalStrategy(host_context_->jingle_thread(), uid,
+ auth_token, auth_service));
+
+ // Request registration of the host for support.
+ scoped_ptr<RegisterSupportHostRequest> register_request(
+ new RegisterSupportHostRequest(
+ signal_strategy.get(), &host_key_pair_,
+ base::Bind(&HostNPScriptObject::OnReceivedSupportID,
+ base::Unretained(this))));
+
+ // Beyond this point nothing can fail, so save the config and request.
+ signal_strategy_.reset(signal_strategy.release());
+ register_request_.reset(register_request.release());
+
+ // Create the Host.
+ LOG(INFO) << "NAT state: " << nat_traversal_enabled_;
+ host_ = new ChromotingHost(
+ host_context_.get(), signal_strategy_.get(), desktop_environment_.get(),
+ protocol::NetworkSettings(nat_traversal_enabled_));
+ host_->AddStatusObserver(this);
+ log_to_server_.reset(
+ new LogToServer(host_, ServerLogEntry::IT2ME, signal_strategy_.get()));
+ it2me_host_user_interface_.reset(
+ new It2MeHostUserInterface(host_.get(), host_context_.get()));
+ it2me_host_user_interface_->Init();
+
+ {
+ base::AutoLock auto_lock(ui_strings_lock_);
+ host_->SetUiStrings(ui_strings_);
+ }
+
+ signal_strategy_->Connect();
+ host_->Start();
+
+ SetState(kRequestedAccessCode);
+ return;
+}
+
+bool HostNPScriptObject::Disconnect(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
+ if (arg_count != 0) {
+ SetException("disconnect: bad number of arguments");
+ return false;
+ }
+
+ DisconnectInternal();
+
+ return true;
+}
+
+bool HostNPScriptObject::Localize(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
+ if (arg_count != 1) {
+ SetException("localize: bad number of arguments");
+ return false;
+ }
+
+ if (NPVARIANT_IS_OBJECT(args[0])) {
+ ScopedRefNPObject localize_func(NPVARIANT_TO_OBJECT(args[0]));
+ LocalizeStrings(localize_func.get());
+ return true;
+ } else {
+ SetException("localize: unexpected type for argument 1");
+ return false;
+ }
+}
+
+bool HostNPScriptObject::SetDaemonPin(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ if (arg_count != 1) {
+ SetException("startDaemon: bad number of arguments");
+ return false;
+ }
+ if (NPVARIANT_IS_STRING(args[0])) {
+ bool set_pin_result =
+ daemon_controller_->SetPin(StringFromNPVariant(args[0]));
+ BOOLEAN_TO_NPVARIANT(set_pin_result, *result);
+ return true;
+ } else {
+ SetException("startDaemon: unexpected type for argument 1");
+ return false;
+ }
+}
+
+bool HostNPScriptObject::StartDaemon(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ if (arg_count != 0) {
+ SetException("startDaemon: bad number of arguments");
+ return false;
+ }
+ bool start_result = daemon_controller_->Start();
+ BOOLEAN_TO_NPVARIANT(start_result, *result);
+ return true;
+}
+
+bool HostNPScriptObject::StopDaemon(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ if (arg_count != 0) {
+ SetException("startDaemon: bad number of arguments");
+ return false;
+ }
+ bool stop_result = daemon_controller_->Stop();
+ BOOLEAN_TO_NPVARIANT(stop_result, *result);
+ return true;
+}
+
+void HostNPScriptObject::DisconnectInternal() {
+ if (!host_context_->network_message_loop()->BelongsToCurrentThread()) {
+ host_context_->network_message_loop()->PostTask(
+ FROM_HERE, base::Bind(&HostNPScriptObject::DisconnectInternal,
+ base::Unretained(this)));
+ return;
+ }
+
+ switch (state_) {
+ case kDisconnected:
+ disconnected_event_.Signal();
+ return;
+
+ case kStarting:
+ SetState(kDisconnecting);
+ SetState(kDisconnected);
+ disconnected_event_.Signal();
+ return;
+
+ case kDisconnecting:
+ return;
+
+ default:
+ DCHECK(host_);
+ SetState(kDisconnecting);
+
+ // ChromotingHost::Shutdown() may destroy SignalStrategy
+ // synchronously, bug SignalStrategy::Listener handlers are not
+ // allowed to destroy SignalStrategy, so post task to call
+ // Shutdown() later.
+ host_context_->network_message_loop()->PostTask(
+ FROM_HERE, base::Bind(
+ &ChromotingHost::Shutdown, host_,
+ base::Bind(&HostNPScriptObject::OnShutdownFinished,
+ base::Unretained(this))));
+ }
+}
+
+void HostNPScriptObject::OnShutdownFinished() {
+ DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
+
+ disconnected_event_.Signal();
+}
+
+void HostNPScriptObject::OnNatPolicyUpdate(bool nat_traversal_enabled) {
+ if (!host_context_->network_message_loop()->BelongsToCurrentThread()) {
+ host_context_->network_message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&HostNPScriptObject::OnNatPolicyUpdate,
+ base::Unretained(this), nat_traversal_enabled));
+ return;
+ }
+
+ VLOG(2) << "OnNatPolicyUpdate: " << nat_traversal_enabled;
+
+ // When transitioning from enabled to disabled, force disconnect any
+ // existing session.
+ if (nat_traversal_enabled_ && !nat_traversal_enabled) {
+ DisconnectInternal();
+ }
+
+ {
+ base::AutoLock lock(nat_policy_lock_);
+ policy_received_ = true;
+ nat_traversal_enabled_ = nat_traversal_enabled;
+ }
+
+ UpdateWebappNatPolicy(nat_traversal_enabled_);
+
+ if (!pending_connect_.is_null()) {
+ pending_connect_.Run();
+ pending_connect_.Reset();
+ }
+}
+
+void HostNPScriptObject::OnReceivedSupportID(
+ bool success,
+ const std::string& support_id,
+ const base::TimeDelta& lifetime) {
+ DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
+
+ if (!success) {
+ SetState(kError);
+ DisconnectInternal();
+ return;
+ }
+
+ std::string host_secret = GenerateSupportHostSecret();
+ std::string access_code = support_id + host_secret;
+ scoped_ptr<protocol::AuthenticatorFactory> factory(
+ new protocol::It2MeHostAuthenticatorFactory(
+ host_key_pair_.GenerateCertificate(), *host_key_pair_.private_key(),
+ access_code));
+ host_->SetAuthenticatorFactory(factory.Pass());
+
+ {
+ base::AutoLock lock(access_code_lock_);
+ access_code_ = access_code;
+ access_code_lifetime_ = lifetime;
+ }
+
+ SetState(kReceivedAccessCode);
+}
+
+void HostNPScriptObject::SetState(State state) {
+ DCHECK(host_context_->network_message_loop()->BelongsToCurrentThread());
+ switch (state_) {
+ case kDisconnected:
+ DCHECK(state == kStarting ||
+ state == kError) << state;
+ break;
+ case kStarting:
+ DCHECK(state == kRequestedAccessCode ||
+ state == kDisconnecting ||
+ state == kError) << state;
+ break;
+ case kRequestedAccessCode:
+ DCHECK(state == kReceivedAccessCode ||
+ state == kDisconnecting ||
+ state == kError) << state;
+ break;
+ case kReceivedAccessCode:
+ DCHECK(state == kConnected ||
+ state == kDisconnecting ||
+ state == kError) << state;
+ break;
+ case kConnected:
+ DCHECK(state == kDisconnecting ||
+ state == kDisconnected ||
+ state == kError) << state;
+ break;
+ case kDisconnecting:
+ DCHECK(state == kDisconnected) << state;
+ break;
+ case kError:
+ DCHECK(state == kDisconnecting) << state;
+ break;
+ };
+ state_ = state;
+ NotifyStateChanged(state);
+}
+
+void HostNPScriptObject::NotifyStateChanged(State state) {
+ if (!plugin_message_loop_proxy_->BelongsToCurrentThread()) {
+ plugin_message_loop_proxy_->PostTask(
+ FROM_HERE, base::Bind(&HostNPScriptObject::NotifyStateChanged,
+ base::Unretained(this), state));
+ return;
+ }
+ if (on_state_changed_func_.get()) {
+ VLOG(2) << "Calling state changed " << state;
+ NPVariant state_var;
+ INT32_TO_NPVARIANT(state, state_var);
+ bool is_good = InvokeAndIgnoreResult(on_state_changed_func_.get(),
+ &state_var, 1);
+ LOG_IF(ERROR, !is_good) << "OnStateChanged failed";
+ }
+}
+
+void HostNPScriptObject::PostLogDebugInfo(const std::string& message) {
+ if (plugin_message_loop_proxy_->BelongsToCurrentThread()) {
+ // Make sure we're not currently processing a log message.
+ // We only need to check this if we're on the plugin thread.
+ if (am_currently_logging_)
+ return;
+ }
+
+ // Always post (even if we're already on the correct thread) so that debug
+ // log messages are shown in the correct order.
+ plugin_message_loop_proxy_->PostTask(
+ FROM_HERE, base::Bind(&HostNPScriptObject::LogDebugInfo,
+ base::Unretained(this), message));
+}
+
+void HostNPScriptObject::LogDebugInfo(const std::string& message) {
+ DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
+ if (log_debug_info_func_.get()) {
+ am_currently_logging_ = true;
+ NPVariant log_message;
+ STRINGZ_TO_NPVARIANT(message.c_str(), log_message);
+ bool is_good = InvokeAndIgnoreResult(log_debug_info_func_.get(),
+ &log_message, 1);
+ if (!is_good) {
+ LOG(ERROR) << "ERROR - LogDebugInfo failed\n";
+ }
+ am_currently_logging_ = false;
+ }
+}
+
+void HostNPScriptObject::SetException(const std::string& exception_string) {
+ DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
+ g_npnetscape_funcs->setexception(parent_, exception_string.c_str());
+ LOG(INFO) << exception_string;
+}
+
+void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) {
+ DCHECK(plugin_message_loop_proxy_->BelongsToCurrentThread());
+
+ UiStrings ui_strings;
+ string16 direction;
+ LocalizeString(localize_func, "@@bidi_dir", &direction);
+ ui_strings.direction = UTF16ToUTF8(direction) == "rtl" ?
+ remoting::UiStrings::RTL : remoting::UiStrings::LTR;
+ LocalizeString(localize_func, /*i18n-content*/"PRODUCT_NAME",
+ &ui_strings.product_name);
+ LocalizeString(localize_func, /*i18n-content*/"DISCONNECT_OTHER_BUTTON",
+ &ui_strings.disconnect_button_text);
+ LocalizeString(localize_func,
+#if defined(OS_WIN)
+ /*i18n-content*/"DISCONNECT_BUTTON_PLUS_SHORTCUT_WINDOWS",
+#elif defined(OS_MACOSX)
+ /*i18n-content*/"DISCONNECT_BUTTON_PLUS_SHORTCUT_MAC_OS_X",
+#else
+ /*i18n-content*/"DISCONNECT_BUTTON_PLUS_SHORTCUT_LINUX",
+#endif
+ &ui_strings.disconnect_button_text_plus_shortcut);
+ LocalizeString(localize_func, /*i18n-content*/"CONTINUE_PROMPT",
+ &ui_strings.continue_prompt);
+ LocalizeString(localize_func, /*i18n-content*/"CONTINUE_BUTTON",
+ &ui_strings.continue_button_text);
+ LocalizeString(localize_func, /*i18n-content*/"STOP_SHARING_BUTTON",
+ &ui_strings.stop_sharing_button_text);
+ LocalizeString(localize_func, /*i18n-content*/"MESSAGE_SHARED",
+ &ui_strings.disconnect_message);
+
+ base::AutoLock auto_lock(ui_strings_lock_);
+ ui_strings_ = ui_strings;
+}
+
+bool HostNPScriptObject::LocalizeString(NPObject* localize_func,
+ const char* tag, string16* result) {
+ NPVariant args[2];
+ STRINGZ_TO_NPVARIANT(tag, args[0]);
+ NPVariant np_result;
+ bool is_good = g_npnetscape_funcs->invokeDefault(
+ plugin_, localize_func, &args[0], 1, &np_result);
+ if (!is_good) {
+ LOG(ERROR) << "Localization failed for " << tag;
+ return false;
+ }
+ std::string translation = StringFromNPVariant(np_result);
+ g_npnetscape_funcs->releasevariantvalue(&np_result);
+ if (translation.empty()) {
+ LOG(ERROR) << "Missing translation for " << tag;
+ return false;
+ }
+ *result = UTF8ToUTF16(translation);
+ return true;
+}
+
+void HostNPScriptObject::UpdateWebappNatPolicy(bool nat_traversal_enabled) {
+ if (!plugin_message_loop_proxy_->BelongsToCurrentThread()) {
+ plugin_message_loop_proxy_->PostTask(
+ FROM_HERE, base::Bind(&HostNPScriptObject::UpdateWebappNatPolicy,
+ base::Unretained(this), nat_traversal_enabled));
+ return;
+ }
+ if (on_nat_traversal_policy_changed_func_.get()) {
+ NPVariant policy;
+ BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy);
+ InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_.get(),
+ &policy, 1);
+ }
+}
+
+bool HostNPScriptObject::InvokeAndIgnoreResult(NPObject* func,
+ const NPVariant* args,
+ uint32_t argCount) {
+ NPVariant np_result;
+ bool is_good = g_npnetscape_funcs->invokeDefault(plugin_, func, args,
+ argCount, &np_result);
+ if (is_good)
+ g_npnetscape_funcs->releasevariantvalue(&np_result);
+ return is_good;
+}
+
+} // namespace remoting

Powered by Google App Engine
This is Rietveld 408576698