root/remoting/host/it2me/it2me_native_messaging_host.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. weak_factory_
  2. Start
  3. ProcessMessage
  4. ProcessHello
  5. ProcessConnect
  6. ProcessDisconnect
  7. SendErrorAndExit
  8. OnStateChanged
  9. OnNatPolicyChanged
  10. OnStoreAccessCode
  11. OnClientAuthenticated
  12. task_runner
  13. HostStateToString

// Copyright 2013 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/it2me/it2me_native_messaging_host.h"

#include <string>

#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringize_macros.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "net/url_request/url_fetcher.h"
#include "remoting/base/auth_token_util.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/host_exit_codes.h"
#include "remoting/host/service_urls.h"
#include "remoting/protocol/name_value_map.h"

namespace remoting {

namespace {

const remoting::protocol::NameMapElement<It2MeHostState> kIt2MeHostStates[] = {
    {kDisconnected, "DISCONNECTED"},
    {kStarting, "STARTING"},
    {kRequestedAccessCode, "REQUESTED_ACCESS_CODE"},
    {kReceivedAccessCode, "RECEIVED_ACCESS_CODE"},
    {kConnected, "CONNECTED"},
    {kDisconnecting, "DISCONNECTING"},
    {kError, "ERROR"},
    {kInvalidDomainError, "INVALID_DOMAIN_ERROR"}, };

}  // namespace

It2MeNativeMessagingHost::It2MeNativeMessagingHost(
    scoped_refptr<AutoThreadTaskRunner> task_runner,
    scoped_ptr<NativeMessagingChannel> channel,
    scoped_ptr<It2MeHostFactory> factory)
    : channel_(channel.Pass()),
      factory_(factory.Pass()),
      weak_factory_(this) {
  weak_ptr_ = weak_factory_.GetWeakPtr();

  // Initialize the host context to manage the threads for the it2me host.
  // The native messaging host, rather than the It2MeHost object, owns and
  // maintains the lifetime of the host context.

  host_context_.reset(ChromotingHostContext::Create(task_runner).release());

  const ServiceUrls* service_urls = ServiceUrls::GetInstance();
  const bool xmpp_server_valid =
      net::ParseHostAndPort(service_urls->xmpp_server_address(),
                            &xmpp_server_config_.host,
                            &xmpp_server_config_.port);
  DCHECK(xmpp_server_valid);

  xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
  directory_bot_jid_ = service_urls->directory_bot_jid();
}

It2MeNativeMessagingHost::~It2MeNativeMessagingHost() {
  DCHECK(task_runner()->BelongsToCurrentThread());

  if (it2me_host_.get()) {
    it2me_host_->Disconnect();
    it2me_host_ = NULL;
  }
}

void It2MeNativeMessagingHost::Start(const base::Closure& quit_closure) const {
  DCHECK(task_runner()->BelongsToCurrentThread());

  channel_->Start(
      base::Bind(&It2MeNativeMessagingHost::ProcessMessage, weak_ptr_),
      quit_closure);
}

void It2MeNativeMessagingHost::ProcessMessage(
    scoped_ptr<base::DictionaryValue> message) {
  DCHECK(task_runner()->BelongsToCurrentThread());

  scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());

  // If the client supplies an ID, it will expect it in the response. This
  // might be a string or a number, so cope with both.
  const base::Value* id;
  if (message->Get("id", &id))
    response->Set("id", id->DeepCopy());

  std::string type;
  if (!message->GetString("type", &type)) {
    SendErrorAndExit(response.Pass(), "'type' not found in request.");
    return;
  }

  response->SetString("type", type + "Response");

  if (type == "hello") {
    ProcessHello(*message, response.Pass());
  } else if (type == "connect") {
    ProcessConnect(*message, response.Pass());
  } else if (type == "disconnect") {
    ProcessDisconnect(*message, response.Pass());
  } else {
    SendErrorAndExit(response.Pass(), "Unsupported request type: " + type);
  }
}

void It2MeNativeMessagingHost::ProcessHello(
    const base::DictionaryValue& message,
    scoped_ptr<base::DictionaryValue> response) const {
  DCHECK(task_runner()->BelongsToCurrentThread());

  response->SetString("version", STRINGIZE(VERSION));

  // This list will be populated when new features are added.
  scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
  response->Set("supportedFeatures", supported_features_list.release());

  channel_->SendMessage(response.Pass());
}

void It2MeNativeMessagingHost::ProcessConnect(
    const base::DictionaryValue& message,
    scoped_ptr<base::DictionaryValue> response) {
  DCHECK(task_runner()->BelongsToCurrentThread());

  if (it2me_host_.get()) {
    SendErrorAndExit(response.Pass(),
                     "Connect can be called only when disconnected.");
    return;
  }

  XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_;

  if (!message.GetString("userName", &xmpp_config.username)) {
    SendErrorAndExit(response.Pass(), "'userName' not found in request.");
    return;
  }

  std::string auth_service_with_token;
  if (!message.GetString("authServiceWithToken", &auth_service_with_token)) {
    SendErrorAndExit(response.Pass(),
                     "'authServiceWithToken' not found in request.");
    return;
  }

  ParseAuthTokenWithService(auth_service_with_token,
                            &xmpp_config.auth_token,
                            &xmpp_config.auth_service);
  if (xmpp_config.auth_token.empty()) {
    SendErrorAndExit(
        response.Pass(),
        "Invalid 'authServiceWithToken': " + auth_service_with_token);
    return;
  }

#if !defined(NDEBUG)
  std::string address;
  if (!message.GetString("xmppServerAddress", &address)) {
    SendErrorAndExit(response.Pass(),
                     "'xmppServerAddress' not found in request.");
    return;
  }

  if (!net::ParseHostAndPort(
           address, &xmpp_server_config_.host, &xmpp_server_config_.port)) {
    SendErrorAndExit(response.Pass(),
                     "Invalid 'xmppServerAddress': " + address);
    return;
  }

  if (!message.GetBoolean("xmppServerUseTls", &xmpp_server_config_.use_tls)) {
    SendErrorAndExit(response.Pass(),
                     "'xmppServerUseTls' not found in request.");
    return;
  }

  if (!message.GetString("directoryBotJid", &directory_bot_jid_)) {
    SendErrorAndExit(response.Pass(),
                     "'directoryBotJid' not found in request.");
    return;
  }
#endif  // !defined(NDEBUG)

  // Create the It2Me host and start connecting.
  it2me_host_ = factory_->CreateIt2MeHost(host_context_.get(),
                                          host_context_->ui_task_runner(),
                                          weak_ptr_,
                                          xmpp_config,
                                          directory_bot_jid_);
  it2me_host_->Connect();

  channel_->SendMessage(response.Pass());
}

void It2MeNativeMessagingHost::ProcessDisconnect(
    const base::DictionaryValue& message,
    scoped_ptr<base::DictionaryValue> response) {
  DCHECK(task_runner()->BelongsToCurrentThread());

  if (it2me_host_.get()) {
    it2me_host_->Disconnect();
    it2me_host_ = NULL;
  }
  channel_->SendMessage(response.Pass());
}

void It2MeNativeMessagingHost::SendErrorAndExit(
    scoped_ptr<base::DictionaryValue> response,
    const std::string& description) const {
  DCHECK(task_runner()->BelongsToCurrentThread());

  LOG(ERROR) << description;

  response->SetString("type", "error");
  response->SetString("description", description);
  channel_->SendMessage(response.Pass());

  // Trigger a host shutdown by sending a NULL message.
  channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
}

void It2MeNativeMessagingHost::OnStateChanged(It2MeHostState state) {
  DCHECK(task_runner()->BelongsToCurrentThread());

  state_ = state;

  scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue());

  message->SetString("type", "hostStateChanged");
  message->SetString("state",
                     It2MeNativeMessagingHost::HostStateToString(state));

  switch (state_) {
    case kReceivedAccessCode:
      message->SetString("accessCode", access_code_);
      message->SetInteger("accessCodeLifetime",
                          access_code_lifetime_.InSeconds());
      break;

    case kConnected:
      message->SetString("client", client_username_);
      break;

    case kDisconnected:
      client_username_.clear();
      break;

    default:
      ;
  }

  channel_->SendMessage(message.Pass());
}

void It2MeNativeMessagingHost::OnNatPolicyChanged(bool nat_traversal_enabled) {
  DCHECK(task_runner()->BelongsToCurrentThread());

  scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue());

  message->SetString("type", "natPolicyChanged");
  message->SetBoolean("natTraversalEnabled", nat_traversal_enabled);
  channel_->SendMessage(message.Pass());
}

// Stores the Access Code for the web-app to query.
void It2MeNativeMessagingHost::OnStoreAccessCode(
    const std::string& access_code,
    base::TimeDelta access_code_lifetime) {
  DCHECK(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 It2MeNativeMessagingHost::OnClientAuthenticated(
    const std::string& client_username) {
  DCHECK(task_runner()->BelongsToCurrentThread());

  client_username_ = client_username;
}

scoped_refptr<AutoThreadTaskRunner>
It2MeNativeMessagingHost::task_runner() const {
  return host_context_->ui_task_runner();
}

/* static */
std::string It2MeNativeMessagingHost::HostStateToString(
    It2MeHostState host_state) {
  return ValueToName(kIt2MeHostStates, host_state);
}

}  // namespace remoting


/* [<][>][^][v][top][bottom][index][help] */