blob: c4572e20cdbafe60119d16db26133ec7f372e1e6 [file] [log] [blame]
// 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 "base/bind.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
#include "base/values.h"
#include "net/base/net_util.h"
#include "net/socket/client_socket_factory.h"
#include "remoting/base/auth_token_util.h"
#include "remoting/base/auto_thread.h"
#include "remoting/base/resources.h"
#include "remoting/base/rsa_key_pair.h"
#include "remoting/host/chromoting_host.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/host_config.h"
#include "remoting/host/host_event_logger.h"
#include "remoting/host/host_secret.h"
#include "remoting/host/host_status_observer.h"
#include "remoting/host/it2me_desktop_environment.h"
#include "remoting/host/pairing_registry_delegate.h"
#include "remoting/host/pin_hash.h"
#include "remoting/host/plugin/host_log_handler.h"
#include "remoting/host/policy_hack/policy_watcher.h"
#include "remoting/host/register_support_host_request.h"
#include "remoting/host/service_urls.h"
#include "remoting/host/session_manager_factory.h"
#include "remoting/jingle_glue/network_settings.h"
#include "remoting/jingle_glue/xmpp_signal_strategy.h"
#include "remoting/protocol/it2me_host_authenticator_factory.h"
#include "third_party/npapi/bindings/npruntime.h"
namespace remoting {
namespace {
// This is used for tagging system event logs.
const char kApplicationName[] = "chromoting";
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* kAttrNameXmppServerAddress = "xmppServerAddress";
const char* kAttrNameXmppServerUseTls = "xmppServerUseTls";
const char* kAttrNameDirectoryBotJid = "directoryBotJid";
const char* kAttrNameSupportedFeatures = "supportedFeatures";
const char* kFuncNameConnect = "connect";
const char* kFuncNameDisconnect = "disconnect";
const char* kFuncNameLocalize = "localize";
const char* kFuncNameClearPairedClients = "clearPairedClients";
const char* kFuncNameDeletePairedClient = "deletePairedClient";
const char* kFuncNameGetHostName = "getHostName";
const char* kFuncNameGetPinHash = "getPinHash";
const char* kFuncNameGenerateKeyPair = "generateKeyPair";
const char* kFuncNameUpdateDaemonConfig = "updateDaemonConfig";
const char* kFuncNameGetDaemonConfig = "getDaemonConfig";
const char* kFuncNameGetDaemonVersion = "getDaemonVersion";
const char* kFuncNameGetPairedClients = "getPairedClients";
const char* kFuncNameGetUsageStatsConsent = "getUsageStatsConsent";
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 char* kAttrNameInvalidDomainError = "INVALID_DOMAIN_ERROR";
const int kMaxLoginAttempts = 5;
// Space separated list of features supported in addition to the base protocol.
const char* kSupportedFeatures = "pairingRegistry";
} // namespace
// Internal implementation of the plugin's It2Me host function.
class HostNPScriptObject::It2MeImpl
: public base::RefCountedThreadSafe<It2MeImpl>,
public HostStatusObserver {
public:
It2MeImpl(
scoped_ptr<ChromotingHostContext> context,
scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner,
base::WeakPtr<HostNPScriptObject> script_object,
const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
const std::string& directory_bot_jid);
// Methods called by the script object, from the plugin thread.
// Creates It2Me host structures and starts the host.
void Connect();
// Disconnects the host, ready for tear-down.
// Also called internally, from the network thread.
void Disconnect();
// Request a NAT policy notification.
void RequestNatPolicy();
// remoting::HostStatusObserver implementation.
virtual void OnAccessDenied(const std::string& jid) OVERRIDE;
virtual void OnClientAuthenticated(const std::string& jid) OVERRIDE;
virtual void OnClientDisconnected(const std::string& jid) OVERRIDE;
private:
friend class base::RefCountedThreadSafe<It2MeImpl>;
virtual ~It2MeImpl();
// Updates state of the host. Can be called only on the network thread.
void SetState(State state);
// Returns true if the host is connected.
bool IsConnected() const;
// Called by Connect() to check for policies and start connection process.
void ReadPolicyAndConnect();
// Called by ReadPolicyAndConnect once policies have been read.
void FinishConnect();
// Called when the support host registration completes.
void OnReceivedSupportID(bool success,
const std::string& support_id,
const base::TimeDelta& lifetime);
// Shuts down |host_| on the network thread and posts ShutdownOnUiThread()
// to shut down UI thread resources.
void ShutdownOnNetworkThread();
// Shuts down |desktop_environment_factory_| and |policy_watcher_| on
// the UI thread.
void ShutdownOnUiThread();
// Called when initial policies are read, and when they change.
void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies);
// Handlers for NAT traversal and host domain policies.
void UpdateNatPolicy(bool nat_traversal_enabled);
void UpdateHostDomainPolicy(const std::string& host_domain);
// Caller supplied fields.
scoped_ptr<ChromotingHostContext> host_context_;
scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner_;
base::WeakPtr<HostNPScriptObject> script_object_;
XmppSignalStrategy::XmppServerConfig xmpp_server_config_;
std::string directory_bot_jid_;
State state_;
scoped_refptr<RsaKeyPair> host_key_pair_;
scoped_ptr<SignalStrategy> signal_strategy_;
scoped_ptr<RegisterSupportHostRequest> register_request_;
scoped_ptr<LogToServer> log_to_server_;
scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_;
scoped_ptr<HostEventLogger> host_event_logger_;
scoped_ptr<ChromotingHost> host_;
int failed_login_attempts_;
scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_;
// Host the current nat traversal policy setting.
bool nat_traversal_enabled_;
// The host domain policy setting.
std::string required_host_domain_;
// Indicates whether or not a policy has ever been read. This is to ensure
// that on startup, we do not accidentally start a connection before we have
// queried our policy restrictions.
bool policy_received_;
// On startup, it is possible to have Connect() called before the policy read
// is completed. Rather than just failing, we thunk the connection call so
// it can be executed after at least one successful policy read. This
// variable contains the thunk if it is necessary.
base::Closure pending_connect_;
DISALLOW_COPY_AND_ASSIGN(It2MeImpl);
};
HostNPScriptObject::It2MeImpl::It2MeImpl(
scoped_ptr<ChromotingHostContext> host_context,
scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner,
base::WeakPtr<HostNPScriptObject> script_object,
const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
const std::string& directory_bot_jid)
: host_context_(host_context.Pass()),
plugin_task_runner_(plugin_task_runner),
script_object_(script_object),
xmpp_server_config_(xmpp_server_config),
directory_bot_jid_(directory_bot_jid),
state_(kDisconnected),
failed_login_attempts_(0),
nat_traversal_enabled_(false),
policy_received_(false) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
}
void HostNPScriptObject::It2MeImpl::Connect() {
if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
host_context_->ui_task_runner()->PostTask(
FROM_HERE, base::Bind(&It2MeImpl::Connect, this));
return;
}
desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory(
host_context_->network_task_runner(),
host_context_->input_task_runner(),
host_context_->ui_task_runner()));
// Start monitoring configured policies.
policy_watcher_.reset(
policy_hack::PolicyWatcher::Create(host_context_->network_task_runner()));
policy_watcher_->StartWatching(
base::Bind(&It2MeImpl::OnPolicyUpdate, this));
// Switch to the network thread to start the actual connection.
host_context_->network_task_runner()->PostTask(
FROM_HERE, base::Bind(&It2MeImpl::ReadPolicyAndConnect, this));
}
void HostNPScriptObject::It2MeImpl::Disconnect() {
if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
host_context_->network_task_runner()->PostTask(
FROM_HERE, base::Bind(&It2MeImpl::Disconnect, this));
return;
}
switch (state_) {
case kDisconnected:
ShutdownOnNetworkThread();
return;
case kStarting:
SetState(kDisconnecting);
SetState(kDisconnected);
ShutdownOnNetworkThread();
return;
case kDisconnecting:
return;
default:
SetState(kDisconnecting);
if (!host_) {
SetState(kDisconnected);
ShutdownOnNetworkThread();
return;
}
// Deleting the host destroys SignalStrategy synchronously, but
// SignalStrategy::Listener handlers are not allowed to destroy
// SignalStrategy, so post task to destroy the host later.
host_context_->network_task_runner()->PostTask(
FROM_HERE, base::Bind(&It2MeImpl::ShutdownOnNetworkThread, this));
return;
}
}
void HostNPScriptObject::It2MeImpl::RequestNatPolicy() {
if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
host_context_->network_task_runner()->PostTask(
FROM_HERE, base::Bind(&It2MeImpl::RequestNatPolicy, this));
return;
}
if (policy_received_)
UpdateNatPolicy(nat_traversal_enabled_);
}
void HostNPScriptObject::It2MeImpl::ReadPolicyAndConnect() {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
SetState(kStarting);
// Only proceed to FinishConnect() if at least one policy update has been
// received.
if (policy_received_) {
FinishConnect();
} else {
// Otherwise, create the policy watcher, and thunk the connect.
pending_connect_ =
base::Bind(&It2MeImpl::FinishConnect, this);
}
}
void HostNPScriptObject::It2MeImpl::FinishConnect() {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
if (state_ != kStarting) {
// Host has been stopped while we were fetching policy.
return;
}
// Check the host domain policy.
if (!required_host_domain_.empty() &&
!EndsWith(xmpp_server_config_.username,
std::string("@") + required_host_domain_, false)) {
SetState(kInvalidDomainError);
return;
}
// Generate a key pair for the Host to use.
// TODO(wez): Move this to the worker thread.
host_key_pair_ = RsaKeyPair::Generate();
// Create XMPP connection.
scoped_ptr<SignalStrategy> signal_strategy(
new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
host_context_->url_request_context_getter(),
xmpp_server_config_));
// Request registration of the host for support.
scoped_ptr<RegisterSupportHostRequest> register_request(
new RegisterSupportHostRequest(
signal_strategy.get(), host_key_pair_, directory_bot_jid_,
base::Bind(&It2MeImpl::OnReceivedSupportID,
base::Unretained(this))));
// Beyond this point nothing can fail, so save the config and request.
signal_strategy_ = signal_strategy.Pass();
register_request_ = register_request.Pass();
// If NAT traversal is off then limit port range to allow firewall pin-holing.
LOG(INFO) << "NAT state: " << nat_traversal_enabled_;
NetworkSettings network_settings(
nat_traversal_enabled_ ?
NetworkSettings::NAT_TRAVERSAL_ENABLED :
NetworkSettings::NAT_TRAVERSAL_DISABLED);
if (!nat_traversal_enabled_) {
network_settings.min_port = NetworkSettings::kDefaultMinPort;
network_settings.max_port = NetworkSettings::kDefaultMaxPort;
}
// Create the host.
host_.reset(new ChromotingHost(
signal_strategy_.get(),
desktop_environment_factory_.get(),
CreateHostSessionManager(network_settings,
host_context_->url_request_context_getter()),
host_context_->audio_task_runner(),
host_context_->input_task_runner(),
host_context_->video_capture_task_runner(),
host_context_->video_encode_task_runner(),
host_context_->network_task_runner(),
host_context_->ui_task_runner()));
host_->AddStatusObserver(this);
log_to_server_.reset(
new LogToServer(host_->AsWeakPtr(), ServerLogEntry::IT2ME,
signal_strategy_.get(), directory_bot_jid_));
// Disable audio by default.
// TODO(sergeyu): Add UI to enable it.
scoped_ptr<protocol::CandidateSessionConfig> protocol_config =
protocol::CandidateSessionConfig::CreateDefault();
protocol::CandidateSessionConfig::DisableAudioChannel(protocol_config.get());
host_->set_protocol_config(protocol_config.Pass());
// Create event logger.
host_event_logger_ =
HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
// Connect signaling and start the host.
signal_strategy_->Connect();
host_->Start(xmpp_server_config_.username);
SetState(kRequestedAccessCode);
return;
}
void HostNPScriptObject::It2MeImpl::ShutdownOnNetworkThread() {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
DCHECK(state_ == kDisconnecting || state_ == kDisconnected);
if (state_ == kDisconnecting) {
host_event_logger_.reset();
host_->RemoveStatusObserver(this);
host_.reset();
register_request_.reset();
log_to_server_.reset();
signal_strategy_.reset();
SetState(kDisconnected);
}
host_context_->ui_task_runner()->PostTask(
FROM_HERE, base::Bind(&It2MeImpl::ShutdownOnUiThread, this));
}
void HostNPScriptObject::It2MeImpl::ShutdownOnUiThread() {
DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread());
// Destroy the DesktopEnvironmentFactory, to free thread references.
desktop_environment_factory_.reset();
// Stop listening for policy updates.
if (policy_watcher_.get()) {
base::WaitableEvent policy_watcher_stopped_(true, false);
policy_watcher_->StopWatching(&policy_watcher_stopped_);
policy_watcher_stopped_.Wait();
policy_watcher_.reset();
}
}
void HostNPScriptObject::It2MeImpl::OnAccessDenied(const std::string& jid) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
++failed_login_attempts_;
if (failed_login_attempts_ == kMaxLoginAttempts) {
Disconnect();
}
}
void HostNPScriptObject::It2MeImpl::OnClientAuthenticated(
const std::string& jid) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
if (state_ == kDisconnecting) {
// Ignore the new connection if we are disconnecting.
return;
}
if (state_ == kConnected) {
// If we already connected another client then one of the connections may be
// an attacker, so both are suspect and we have to reject the second
// connection and shutdown the host.
host_->RejectAuthenticatingClient();
Disconnect();
return;
}
std::string 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.";
// Pass the client user name to the script object before changing state.
plugin_task_runner_->PostTask(
FROM_HERE, base::Bind(&HostNPScriptObject::StoreClientUsername,
script_object_, client_username));
SetState(kConnected);
}
void HostNPScriptObject::It2MeImpl::OnClientDisconnected(
const std::string& jid) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
// Pass the client user name to the script object before changing state.
plugin_task_runner_->PostTask(
FROM_HERE, base::Bind(&HostNPScriptObject::StoreClientUsername,
script_object_, std::string()));
Disconnect();
}
void HostNPScriptObject::It2MeImpl::OnPolicyUpdate(
scoped_ptr<base::DictionaryValue> policies) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
bool nat_policy;
if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
&nat_policy)) {
UpdateNatPolicy(nat_policy);
}
std::string host_domain;
if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
&host_domain)) {
UpdateHostDomainPolicy(host_domain);
}
policy_received_ = true;
if (!pending_connect_.is_null()) {
pending_connect_.Run();
pending_connect_.Reset();
}
}
void HostNPScriptObject::It2MeImpl::UpdateNatPolicy(
bool nat_traversal_enabled) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled;
// When transitioning from enabled to disabled, force disconnect any
// existing session.
if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) {
Disconnect();
}
nat_traversal_enabled_ = nat_traversal_enabled;
// Notify the web-app of the policy setting.
plugin_task_runner_->PostTask(
FROM_HERE, base::Bind(&HostNPScriptObject::NotifyNatPolicyChanged,
script_object_, nat_traversal_enabled_));
}
void HostNPScriptObject::It2MeImpl::UpdateHostDomainPolicy(
const std::string& host_domain) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
VLOG(2) << "UpdateHostDomainPolicy: " << host_domain;
// When setting a host domain policy, force disconnect any existing session.
if (!host_domain.empty() && IsConnected()) {
Disconnect();
}
required_host_domain_ = host_domain;
}
HostNPScriptObject::It2MeImpl::~It2MeImpl() {
// Check that resources that need to be torn down on the UI thread are gone.
DCHECK(!desktop_environment_factory_.get());
DCHECK(!policy_watcher_.get());
}
void HostNPScriptObject::It2MeImpl::SetState(State state) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
switch (state_) {
case kDisconnected:
DCHECK(state == kStarting ||
state == kError) << state;
break;
case kStarting:
DCHECK(state == kRequestedAccessCode ||
state == kDisconnecting ||
state == kError ||
state == kInvalidDomainError) << 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;
case kInvalidDomainError:
DCHECK(state == kDisconnecting) << state;
break;
};
state_ = state;
// Post a state-change notification to the web-app.
plugin_task_runner_->PostTask(
FROM_HERE, base::Bind(&HostNPScriptObject::NotifyStateChanged,
script_object_, state));
}
bool HostNPScriptObject::It2MeImpl::IsConnected() const {
return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode ||
state_ == kConnected;
}
void HostNPScriptObject::It2MeImpl::OnReceivedSupportID(
bool success,
const std::string& support_id,
const base::TimeDelta& lifetime) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
if (!success) {
SetState(kError);
Disconnect();
return;
}
std::string host_secret = GenerateSupportHostSecret();
std::string access_code = support_id + host_secret;
std::string local_certificate = host_key_pair_->GenerateCertificate();
if (local_certificate.empty()) {
LOG(ERROR) << "Failed to generate host certificate.";
SetState(kError);
Disconnect();
return;
}
scoped_ptr<protocol::AuthenticatorFactory> factory(
new protocol::It2MeHostAuthenticatorFactory(
local_certificate, host_key_pair_, access_code));
host_->SetAuthenticatorFactory(factory.Pass());
// Pass the Access Code to the script object before changing state.
plugin_task_runner_->PostTask(
FROM_HERE, base::Bind(&HostNPScriptObject::StoreAccessCode,
script_object_, access_code, lifetime));
SetState(kReceivedAccessCode);
}
HostNPScriptObject::HostNPScriptObject(
NPP plugin,
NPObject* parent,
scoped_refptr<AutoThreadTaskRunner> plugin_task_runner)
: plugin_(plugin),
parent_(parent),
plugin_task_runner_(plugin_task_runner),
am_currently_logging_(false),
state_(kDisconnected),
weak_factory_(this),
weak_ptr_(weak_factory_.GetWeakPtr()) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
// Set the thread task runner for the plugin thread so that timers and other
// code using |base::ThreadTaskRunnerHandle| could be used on the plugin
// thread.
//
// If component build is used, Chrome and the plugin may end up sharing base
// binary. This means that the instance of |base::ThreadTaskRunnerHandle|
// created by Chrome for the current thread is shared as well. This routinely
// happens in the development setting so the below check for
// |!base::ThreadTaskRunnerHandle::IsSet()| is a hack/workaround allowing this
// configuration to work. It lets the plugin to access Chrome's message loop
// directly via |base::ThreadTaskRunnerHandle|. This is safe as long as both
// Chrome and the plugin are built from the same version of the sources.
if (!base::ThreadTaskRunnerHandle::IsSet()) {
plugin_task_runner_handle_.reset(
new base::ThreadTaskRunnerHandle(plugin_task_runner_));
}
daemon_controller_ = DaemonController::Create();
ServiceUrls* service_urls = ServiceUrls::GetInstance();
bool xmpp_server_valid = net::ParseHostAndPort(
service_urls->xmpp_server_address(),
&xmpp_server_config_.host, &xmpp_server_config_.port);
// For the plugin, this is always the default address, which must be valid.
DCHECK(xmpp_server_valid);
xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
directory_bot_jid_ = service_urls->directory_bot_jid();
// Create worker thread for encryption key generation and loading the paired
// clients.
worker_thread_ = AutoThread::Create("ChromotingWorkerThread",
plugin_task_runner_);
pairing_registry_ = CreatePairingRegistry(worker_thread_);
}
HostNPScriptObject::~HostNPScriptObject() {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
HostLogHandler::UnregisterLoggingScriptObject(this);
// Stop the It2Me host if the caller forgot to.
if (it2me_impl_.get()) {
it2me_impl_->Disconnect();
it2me_impl_ = NULL;
}
}
bool HostNPScriptObject::HasMethod(const std::string& method_name) {
VLOG(2) << "HasMethod " << method_name;
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
return (method_name == kFuncNameConnect ||
method_name == kFuncNameDisconnect ||
method_name == kFuncNameLocalize ||
method_name == kFuncNameClearPairedClients ||
method_name == kFuncNameDeletePairedClient ||
method_name == kFuncNameGetHostName ||
method_name == kFuncNameGetPinHash ||
method_name == kFuncNameGenerateKeyPair ||
method_name == kFuncNameUpdateDaemonConfig ||
method_name == kFuncNameGetDaemonConfig ||
method_name == kFuncNameGetDaemonVersion ||
method_name == kFuncNameGetPairedClients ||
method_name == kFuncNameGetUsageStatsConsent ||
method_name == kFuncNameStartDaemon ||
method_name == kFuncNameStopDaemon);
}
bool HostNPScriptObject::InvokeDefault(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
VLOG(2) << "InvokeDefault";
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
SetException("exception during default invocation");
return false;
}
bool HostNPScriptObject::Invoke(const std::string& method_name,
const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
VLOG(2) << "Invoke " << method_name;
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
if (method_name == kFuncNameConnect) {
return Connect(args, arg_count, result);
} else if (method_name == kFuncNameDisconnect) {
return Disconnect(args, arg_count, result);
} else if (method_name == kFuncNameLocalize) {
return Localize(args, arg_count, result);
} else if (method_name == kFuncNameClearPairedClients) {
return ClearPairedClients(args, arg_count, result);
} else if (method_name == kFuncNameDeletePairedClient) {
return DeletePairedClient(args, arg_count, result);
} else if (method_name == kFuncNameGetHostName) {
return GetHostName(args, arg_count, result);
} else if (method_name == kFuncNameGetPinHash) {
return GetPinHash(args, arg_count, result);
} else if (method_name == kFuncNameGenerateKeyPair) {
return GenerateKeyPair(args, arg_count, result);
} else if (method_name == kFuncNameUpdateDaemonConfig) {
return UpdateDaemonConfig(args, arg_count, result);
} else if (method_name == kFuncNameGetDaemonConfig) {
return GetDaemonConfig(args, arg_count, result);
} else if (method_name == kFuncNameGetDaemonVersion) {
return GetDaemonVersion(args, arg_count, result);
} else if (method_name == kFuncNameGetPairedClients) {
return GetPairedClients(args, arg_count, result);
} else if (method_name == kFuncNameGetUsageStatsConsent) {
return GetUsageStatsConsent(args, arg_count, result);
} else if (method_name == kFuncNameStartDaemon) {
return StartDaemon(args, arg_count, result);
} else if (method_name == kFuncNameStopDaemon) {
return StopDaemon(args, arg_count, result);
} else {
SetException("Invoke: unknown method " + method_name);
return false;
}
}
bool HostNPScriptObject::HasProperty(const std::string& property_name) {
VLOG(2) << "HasProperty " << property_name;
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
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 ||
property_name == kAttrNameXmppServerAddress ||
property_name == kAttrNameXmppServerUseTls ||
property_name == kAttrNameDirectoryBotJid ||
property_name == kAttrNameSupportedFeatures);
}
bool HostNPScriptObject::GetProperty(const std::string& property_name,
NPVariant* result) {
VLOG(2) << "GetProperty " << property_name;
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
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) {
*result = NPVariantFromString(access_code_);
return true;
} else if (property_name == kAttrNameAccessCodeLifetime) {
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 if (property_name == kAttrNameInvalidDomainError) {
INT32_TO_NPVARIANT(kInvalidDomainError, *result);
return true;
} else if (property_name == kAttrNameXmppServerAddress) {
*result = NPVariantFromString(base::StringPrintf(
"%s:%u", xmpp_server_config_.host.c_str(), xmpp_server_config_.port));
return true;
} else if (property_name == kAttrNameXmppServerUseTls) {
BOOLEAN_TO_NPVARIANT(xmpp_server_config_.use_tls, *result);
return true;
} else if (property_name == kAttrNameDirectoryBotJid) {
*result = NPVariantFromString(directory_bot_jid_);
return true;
} else if (property_name == kAttrNameSupportedFeatures) {
*result = NPVariantFromString(kSupportedFeatures);
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;
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
if (property_name == kAttrNameOnNatTraversalPolicyChanged) {
if (NPVARIANT_IS_OBJECT(*value)) {
on_nat_traversal_policy_changed_func_ = NPVARIANT_TO_OBJECT(*value);
if (it2me_impl_.get()) {
// Ask the It2Me implementation to notify the web-app of the policy.
it2me_impl_->RequestNatPolicy();
}
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;
}
#if !defined(NDEBUG)
if (property_name == kAttrNameXmppServerAddress) {
if (NPVARIANT_IS_STRING(*value)) {
std::string address = StringFromNPVariant(*value);
bool xmpp_server_valid = net::ParseHostAndPort(
address, &xmpp_server_config_.host, &xmpp_server_config_.port);
if (xmpp_server_valid) {
return true;
} else {
SetException("SetProperty: invalid value for property " +
property_name);
}
} else {
SetException("SetProperty: unexpected type for property " +
property_name);
}
return false;
}
if (property_name == kAttrNameXmppServerUseTls) {
if (NPVARIANT_IS_BOOLEAN(*value)) {
xmpp_server_config_.use_tls = NPVARIANT_TO_BOOLEAN(*value);
return true;
} else {
SetException("SetProperty: unexpected type for property " +
property_name);
}
return false;
}
if (property_name == kAttrNameDirectoryBotJid) {
if (NPVARIANT_IS_STRING(*value)) {
directory_bot_jid_ = StringFromNPVariant(*value);
return true;
} else {
SetException("SetProperty: unexpected type for property " +
property_name);
}
return false;
}
#endif // !defined(NDEBUG)
return false;
}
bool HostNPScriptObject::RemoveProperty(const std::string& property_name) {
VLOG(2) << "RemoveProperty " << property_name;
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
return false;
}
bool HostNPScriptObject::Enumerate(std::vector<std::string>* values) {
VLOG(2) << "Enumerate";
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
const char* entries[] = {
kAttrNameAccessCode,
kAttrNameState,
kAttrNameLogDebugInfo,
kAttrNameOnStateChanged,
kAttrNameDisconnected,
kAttrNameStarting,
kAttrNameRequestedAccessCode,
kAttrNameReceivedAccessCode,
kAttrNameConnected,
kAttrNameDisconnecting,
kAttrNameError,
kAttrNameXmppServerAddress,
kAttrNameXmppServerUseTls,
kAttrNameDirectoryBotJid,
kFuncNameConnect,
kFuncNameDisconnect,
kFuncNameLocalize,
kFuncNameClearPairedClients,
kFuncNameDeletePairedClient,
kFuncNameGetHostName,
kFuncNameGetPinHash,
kFuncNameGenerateKeyPair,
kFuncNameUpdateDaemonConfig,
kFuncNameGetDaemonConfig,
kFuncNameGetDaemonVersion,
kFuncNameGetPairedClients,
kFuncNameGetUsageStatsConsent,
kFuncNameStartDaemon,
kFuncNameStopDaemon
};
for (size_t i = 0; i < arraysize(entries); ++i) {
values->push_back(entries[i]);
}
return true;
}
// string username, string auth_token
bool HostNPScriptObject::Connect(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
LOG(INFO) << "Connecting...";
if (arg_count != 2) {
SetException("connect: bad number of arguments");
return false;
}
if (it2me_impl_.get()) {
SetException("connect: can be called only when disconnected");
return false;
}
XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_;
xmpp_config.username = StringFromNPVariant(args[0]);
if (xmpp_config.username.empty()) {
SetException("connect: bad username argument");
return false;
}
std::string auth_service_with_token = StringFromNPVariant(args[1]);
ParseAuthTokenWithService(auth_service_with_token, &xmpp_config.auth_token,
&xmpp_config.auth_service);
if (xmpp_config.auth_token.empty()) {
SetException("connect: auth_service_with_token argument has empty token");
return false;
}
// Create threads for the Chromoting host & desktop environment to use.
scoped_ptr<ChromotingHostContext> host_context =
ChromotingHostContext::Create(plugin_task_runner_);
if (!host_context) {
SetException("connect: failed to start threads");
return false;
}
// Create the It2Me host implementation and start connecting.
it2me_impl_ = new It2MeImpl(
host_context.Pass(), plugin_task_runner_, weak_ptr_,
xmpp_config, directory_bot_jid_);
it2me_impl_->Connect();
return true;
}
bool HostNPScriptObject::Disconnect(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
if (arg_count != 0) {
SetException("disconnect: bad number of arguments");
return false;
}
if (it2me_impl_.get()) {
it2me_impl_->Disconnect();
it2me_impl_ = NULL;
}
return true;
}
bool HostNPScriptObject::Localize(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
DCHECK(plugin_task_runner_->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::ClearPairedClients(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 1) {
SetException("clearPairedClients: bad number of arguments");
return false;
}
if (!NPVARIANT_IS_OBJECT(args[0])) {
SetException("clearPairedClients: invalid callback parameter");
return false;
}
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
if (pairing_registry_) {
pairing_registry_->ClearAllPairings(
base::Bind(&HostNPScriptObject::InvokeBooleanCallback, weak_ptr_,
base::Passed(&callback_obj)));
} else {
InvokeBooleanCallback(callback_obj.Pass(), false);
}
return true;
}
bool HostNPScriptObject::DeletePairedClient(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 2) {
SetException("deletePairedClient: bad number of arguments");
return false;
}
if (!NPVARIANT_IS_STRING(args[0])) {
SetException("deletePairedClient: bad clientId parameter");
return false;
}
if (!NPVARIANT_IS_OBJECT(args[1])) {
SetException("deletePairedClient: invalid callback parameter");
return false;
}
std::string client_id = StringFromNPVariant(args[0]);
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[1])));
if (pairing_registry_) {
pairing_registry_->DeletePairing(
client_id,
base::Bind(&HostNPScriptObject::InvokeBooleanCallback,
weak_ptr_, base::Passed(&callback_obj)));
} else {
InvokeBooleanCallback(callback_obj.Pass(), false);
}
return true;
}
bool HostNPScriptObject::GetHostName(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 1) {
SetException("getHostName: bad number of arguments");
return false;
}
ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[0]));
if (!callback_obj.get()) {
SetException("getHostName: invalid callback parameter");
return false;
}
NPVariant host_name_val = NPVariantFromString(net::GetHostName());
InvokeAndIgnoreResult(callback_obj, &host_name_val, 1);
g_npnetscape_funcs->releasevariantvalue(&host_name_val);
return true;
}
bool HostNPScriptObject::GetPinHash(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 3) {
SetException("getPinHash: bad number of arguments");
return false;
}
std::string host_id = StringFromNPVariant(args[0]);
if (host_id.empty()) {
SetException("getPinHash: bad hostId parameter");
return false;
}
if (!NPVARIANT_IS_STRING(args[1])) {
SetException("getPinHash: bad pin parameter");
return false;
}
std::string pin = StringFromNPVariant(args[1]);
ScopedRefNPObject callback_obj(ObjectFromNPVariant(args[2]));
if (!callback_obj.get()) {
SetException("getPinHash: invalid callback parameter");
return false;
}
NPVariant pin_hash_val = NPVariantFromString(
remoting::MakeHostPinHash(host_id, pin));
InvokeAndIgnoreResult(callback_obj, &pin_hash_val, 1);
g_npnetscape_funcs->releasevariantvalue(&pin_hash_val);
return true;
}
bool HostNPScriptObject::GenerateKeyPair(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 1) {
SetException("generateKeyPair: bad number of arguments");
return false;
}
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
if (!callback_obj->get()) {
SetException("generateKeyPair: invalid callback parameter");
return false;
}
base::Callback<void (const std::string&,
const std::string&)> wrapped_callback =
base::Bind(&HostNPScriptObject::InvokeGenerateKeyPairCallback, weak_ptr_,
base::Passed(&callback_obj));
worker_thread_->PostTask(
FROM_HERE, base::Bind(&HostNPScriptObject::DoGenerateKeyPair,
plugin_task_runner_, wrapped_callback));
return true;
}
bool HostNPScriptObject::UpdateDaemonConfig(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 2) {
SetException("updateDaemonConfig: bad number of arguments");
return false;
}
std::string config_str = StringFromNPVariant(args[0]);
scoped_ptr<base::Value> config(
base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS));
if (config_str.empty() || !config.get() ||
!config->IsType(base::Value::TYPE_DICTIONARY)) {
SetException("updateDaemonConfig: bad config parameter");
return false;
}
scoped_ptr<base::DictionaryValue> config_dict(
reinterpret_cast<base::DictionaryValue*>(config.release()));
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[1])));
if (!callback_obj->get()) {
SetException("updateDaemonConfig: invalid callback parameter");
return false;
}
if (config_dict->HasKey(kHostIdConfigPath) ||
config_dict->HasKey(kXmppLoginConfigPath)) {
SetException("updateDaemonConfig: trying to update immutable config "
"parameters");
return false;
}
daemon_controller_->UpdateConfig(
config_dict.Pass(),
base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
base::Passed(&callback_obj)));
return true;
}
bool HostNPScriptObject::GetDaemonConfig(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 1) {
SetException("getDaemonConfig: bad number of arguments");
return false;
}
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
if (!callback_obj->get()) {
SetException("getDaemonConfig: invalid callback parameter");
return false;
}
daemon_controller_->GetConfig(
base::Bind(&HostNPScriptObject::InvokeGetDaemonConfigCallback, weak_ptr_,
base::Passed(&callback_obj)));
return true;
}
bool HostNPScriptObject::GetDaemonVersion(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 1) {
SetException("getDaemonVersion: bad number of arguments");
return false;
}
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
if (!callback_obj->get()) {
SetException("getDaemonVersion: invalid callback parameter");
return false;
}
daemon_controller_->GetVersion(
base::Bind(&HostNPScriptObject::InvokeGetDaemonVersionCallback, weak_ptr_,
base::Passed(&callback_obj)));
return true;
}
bool HostNPScriptObject::GetPairedClients(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 1) {
SetException("getPairedClients: bad number of arguments");
return false;
}
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
if (!callback_obj->get()) {
SetException("getPairedClients: invalid callback parameter");
return false;
}
if (pairing_registry_) {
pairing_registry_->GetAllPairings(
base::Bind(&HostNPScriptObject::InvokeGetPairedClientsCallback,
weak_ptr_, base::Passed(&callback_obj)));
} else {
scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
InvokeGetPairedClientsCallback(callback_obj.Pass(),
no_paired_clients.Pass());
}
return true;
}
bool HostNPScriptObject::GetUsageStatsConsent(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
if (arg_count != 1) {
SetException("getUsageStatsConsent: bad number of arguments");
return false;
}
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
if (!callback_obj->get()) {
SetException("getUsageStatsConsent: invalid callback parameter");
return false;
}
daemon_controller_->GetUsageStatsConsent(
base::Bind(&HostNPScriptObject::InvokeGetUsageStatsConsentCallback,
weak_ptr_, base::Passed(&callback_obj)));
return true;
}
bool HostNPScriptObject::StartDaemon(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
if (arg_count != 3) {
SetException("startDaemon: bad number of arguments");
return false;
}
std::string config_str = StringFromNPVariant(args[0]);
scoped_ptr<base::Value> config(
base::JSONReader::Read(config_str, base::JSON_ALLOW_TRAILING_COMMAS));
if (config_str.empty() || !config.get() ||
!config->IsType(base::Value::TYPE_DICTIONARY)) {
SetException("startDaemon: bad config parameter");
return false;
}
scoped_ptr<base::DictionaryValue> config_dict(
reinterpret_cast<base::DictionaryValue*>(config.release()));
if (!NPVARIANT_IS_BOOLEAN(args[1])) {
SetException("startDaemon: invalid consent parameter");
return false;
}
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[2])));
if (!callback_obj->get()) {
SetException("startDaemon: invalid callback parameter");
return false;
}
daemon_controller_->SetConfigAndStart(
config_dict.Pass(),
NPVARIANT_TO_BOOLEAN(args[1]),
base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
base::Passed(&callback_obj)));
return true;
}
bool HostNPScriptObject::StopDaemon(const NPVariant* args,
uint32_t arg_count,
NPVariant* result) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
if (arg_count != 1) {
SetException("stopDaemon: bad number of arguments");
return false;
}
scoped_ptr<ScopedRefNPObject> callback_obj(
new ScopedRefNPObject(ObjectFromNPVariant(args[0])));
if (!callback_obj->get()) {
SetException("stopDaemon: invalid callback parameter");
return false;
}
daemon_controller_->Stop(
base::Bind(&HostNPScriptObject::InvokeAsyncResultCallback, weak_ptr_,
base::Passed(&callback_obj)));
return true;
}
void HostNPScriptObject::NotifyStateChanged(State state) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
state_ = state;
if (on_state_changed_func_.get()) {
NPVariant state_var;
INT32_TO_NPVARIANT(state, state_var);
InvokeAndIgnoreResult(on_state_changed_func_, &state_var, 1);
}
}
void HostNPScriptObject::NotifyNatPolicyChanged(bool nat_traversal_enabled) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
if (on_nat_traversal_policy_changed_func_.get()) {
NPVariant policy;
BOOLEAN_TO_NPVARIANT(nat_traversal_enabled, policy);
InvokeAndIgnoreResult(on_nat_traversal_policy_changed_func_,
&policy, 1);
}
}
// Stores the Access Code for the web-app to query.
void HostNPScriptObject::StoreAccessCode(const std::string& access_code,
base::TimeDelta access_code_lifetime) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
access_code_ = access_code;
access_code_lifetime_ = access_code_lifetime;
}
// Stores the client user's name for the web-app to query.
void HostNPScriptObject::StoreClientUsername(
const std::string& client_username) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
client_username_ = client_username;
}
void HostNPScriptObject::PostLogDebugInfo(const std::string& message) {
if (plugin_task_runner_->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_task_runner_->PostTask(
FROM_HERE, base::Bind(&HostNPScriptObject::LogDebugInfo,
weak_ptr_, message));
}
void HostNPScriptObject::SetWindow(NPWindow* np_window) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
daemon_controller_->SetWindow(np_window->window);
}
void HostNPScriptObject::LocalizeStrings(NPObject* localize_func) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
// Reload resources for the current locale. The default UI locale is used on
// Windows.
#if !defined(OS_WIN)
string16 ui_locale;
LocalizeString(localize_func, "@@ui_locale", &ui_locale);
remoting::LoadResources(UTF16ToUTF8(ui_locale));
#endif // !defined(OS_WIN)
}
bool HostNPScriptObject::LocalizeString(NPObject* localize_func,
const char* tag, string16* result) {
return LocalizeStringWithSubstitution(localize_func, tag, NULL, result);
}
bool HostNPScriptObject::LocalizeStringWithSubstitution(
NPObject* localize_func,
const char* tag,
const char* substitution,
string16* result) {
int argc = substitution ? 2 : 1;
scoped_ptr<NPVariant[]> args(new NPVariant[argc]);
STRINGZ_TO_NPVARIANT(tag, args[0]);
if (substitution) {
STRINGZ_TO_NPVARIANT(substitution, args[1]);
}
NPVariant np_result;
bool is_good = g_npnetscape_funcs->invokeDefault(
plugin_, localize_func, args.get(), argc, &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;
}
// static
void HostNPScriptObject::DoGenerateKeyPair(
const scoped_refptr<AutoThreadTaskRunner>& plugin_task_runner,
const base::Callback<void (const std::string&,
const std::string&)>& callback) {
scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
plugin_task_runner->PostTask(FROM_HERE,
base::Bind(callback, key_pair->ToString(),
key_pair->GetPublicKey()));
}
void HostNPScriptObject::InvokeGenerateKeyPairCallback(
scoped_ptr<ScopedRefNPObject> callback,
const std::string& private_key,
const std::string& public_key) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
NPVariant params[2];
params[0] = NPVariantFromString(private_key);
params[1] = NPVariantFromString(public_key);
InvokeAndIgnoreResult(*callback, params, arraysize(params));
g_npnetscape_funcs->releasevariantvalue(&(params[0]));
g_npnetscape_funcs->releasevariantvalue(&(params[1]));
}
void HostNPScriptObject::InvokeAsyncResultCallback(
scoped_ptr<ScopedRefNPObject> callback,
DaemonController::AsyncResult result) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
NPVariant result_var;
INT32_TO_NPVARIANT(static_cast<int32>(result), result_var);
InvokeAndIgnoreResult(*callback, &result_var, 1);
g_npnetscape_funcs->releasevariantvalue(&result_var);
}
void HostNPScriptObject::InvokeBooleanCallback(
scoped_ptr<ScopedRefNPObject> callback, bool result) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
NPVariant result_var;
BOOLEAN_TO_NPVARIANT(result, result_var);
InvokeAndIgnoreResult(*callback, &result_var, 1);
g_npnetscape_funcs->releasevariantvalue(&result_var);
}
void HostNPScriptObject::InvokeGetDaemonConfigCallback(
scoped_ptr<ScopedRefNPObject> callback,
scoped_ptr<base::DictionaryValue> config) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
// There is no easy way to create a dictionary from an NPAPI plugin
// so we have to serialize the dictionary to pass it to JavaScript.
std::string config_str;
if (config.get())
base::JSONWriter::Write(config.get(), &config_str);
NPVariant config_val = NPVariantFromString(config_str);
InvokeAndIgnoreResult(*callback, &config_val, 1);
g_npnetscape_funcs->releasevariantvalue(&config_val);
}
void HostNPScriptObject::InvokeGetDaemonVersionCallback(
scoped_ptr<ScopedRefNPObject> callback, const std::string& version) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
NPVariant version_val = NPVariantFromString(version);
InvokeAndIgnoreResult(*callback, &version_val, 1);
g_npnetscape_funcs->releasevariantvalue(&version_val);
}
void HostNPScriptObject::InvokeGetPairedClientsCallback(
scoped_ptr<ScopedRefNPObject> callback,
scoped_ptr<base::ListValue> paired_clients) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
std::string paired_clients_json;
base::JSONWriter::Write(paired_clients.get(), &paired_clients_json);
NPVariant paired_clients_val = NPVariantFromString(paired_clients_json);
InvokeAndIgnoreResult(*callback, &paired_clients_val, 1);
g_npnetscape_funcs->releasevariantvalue(&paired_clients_val);
}
void HostNPScriptObject::InvokeGetUsageStatsConsentCallback(
scoped_ptr<ScopedRefNPObject> callback,
const DaemonController::UsageStatsConsent& consent) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
NPVariant params[3];
BOOLEAN_TO_NPVARIANT(consent.supported, params[0]);
BOOLEAN_TO_NPVARIANT(consent.allowed, params[1]);
BOOLEAN_TO_NPVARIANT(consent.set_by_policy, params[2]);
InvokeAndIgnoreResult(*callback, params, arraysize(params));
g_npnetscape_funcs->releasevariantvalue(&(params[0]));
g_npnetscape_funcs->releasevariantvalue(&(params[1]));
g_npnetscape_funcs->releasevariantvalue(&(params[2]));
}
void HostNPScriptObject::LogDebugInfo(const std::string& message) {
DCHECK(plugin_task_runner_->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_,
&log_message, 1);
if (!is_good) {
LOG(ERROR) << "ERROR - LogDebugInfo failed\n";
}
am_currently_logging_ = false;
}
}
bool HostNPScriptObject::InvokeAndIgnoreResult(const ScopedRefNPObject& func,
const NPVariant* args,
uint32_t arg_count) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
NPVariant np_result;
bool is_good = g_npnetscape_funcs->invokeDefault(plugin_, func.get(), args,
arg_count, &np_result);
if (is_good)
g_npnetscape_funcs->releasevariantvalue(&np_result);
return is_good;
}
void HostNPScriptObject::SetException(const std::string& exception_string) {
DCHECK(plugin_task_runner_->BelongsToCurrentThread());
g_npnetscape_funcs->setexception(parent_, exception_string.c_str());
LOG(INFO) << exception_string;
}
} // namespace remoting