text: // Copyright 2014 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 "extensions/browser/api/sockets_udp/sockets_udp_api.h"
  
  #include "content/public/common/socket_permission_request.h"
  #include "extensions/browser/api/socket/udp_socket.h"
  #include "extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h"
  #include "extensions/common/api/sockets/sockets_manifest_data.h"
  #include "net/base/net_errors.h"
  
  namespace extensions {
  namespace core_api {
  
  using content::SocketPermissionRequest;
  
  const char kSocketNotFoundError[] = "Socket not found";
  const char kPermissionError[] = "App does not have permission";
  const char kWildcardAddress[] = "*";
  const int kWildcardPort = 0;
  
  UDPSocketAsyncApiFunction::~UDPSocketAsyncApiFunction() {}
  
  scoped_ptr<SocketResourceManagerInterface>
  UDPSocketAsyncApiFunction::CreateSocketResourceManager() {
    return scoped_ptr<SocketResourceManagerInterface>(
               new SocketResourceManager<ResumableUDPSocket>()).Pass();
  }
  
  ResumableUDPSocket* UDPSocketAsyncApiFunction::GetUdpSocket(int socket_id) {
    return static_cast<ResumableUDPSocket*>(GetSocket(socket_id));
  }
  
  UDPSocketExtensionWithDnsLookupFunction::
      ~UDPSocketExtensionWithDnsLookupFunction() {}
  
  scoped_ptr<SocketResourceManagerInterface>
  UDPSocketExtensionWithDnsLookupFunction::CreateSocketResourceManager() {
    return scoped_ptr<SocketResourceManagerInterface>(
               new SocketResourceManager<ResumableUDPSocket>()).Pass();
  }
  
  ResumableUDPSocket* UDPSocketExtensionWithDnsLookupFunction::GetUdpSocket(
      int socket_id) {
    return static_cast<ResumableUDPSocket*>(GetSocket(socket_id));
  }
  
  linked_ptr<sockets_udp::SocketInfo> CreateSocketInfo(
      int socket_id,
      ResumableUDPSocket* socket) {
    linked_ptr<sockets_udp::SocketInfo> socket_info(
        new sockets_udp::SocketInfo());
    // This represents what we know about the socket, and does not call through
    // to the system.
    socket_info->socket_id = socket_id;
    if (!socket->name().empty()) {
      socket_info->name.reset(new std::string(socket->name()));
    }
    socket_info->persistent = socket->persistent();
    if (socket->buffer_size() > 0) {
      socket_info->buffer_size.reset(new int(socket->buffer_size()));
    }
    socket_info->paused = socket->paused();
  
    // Grab the local address as known by the OS.
    net::IPEndPoint localAddress;
    if (socket->GetLocalAddress(&localAddress)) {
      socket_info->local_address.reset(
          new std::string(localAddress.ToStringWithoutPort()));
      socket_info->local_port.reset(new int(localAddress.port()));
    }
  
    return socket_info;
  }
  
  void SetSocketProperties(ResumableUDPSocket* socket,
                           sockets_udp::SocketProperties* properties) {
    if (properties->name.get()) {
      socket->set_name(*properties->name.get());
    }
    if (properties->persistent.get()) {
      socket->set_persistent(*properties->persistent.get());
    }
    if (properties->buffer_size.get()) {
      socket->set_buffer_size(*properties->buffer_size.get());
    }
  }
  
  SocketsUdpCreateFunction::SocketsUdpCreateFunction() {}
  
  SocketsUdpCreateFunction::~SocketsUdpCreateFunction() {}
  
  bool SocketsUdpCreateFunction::Prepare() {
    params_ = sockets_udp::Create::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpCreateFunction::Work() {
    ResumableUDPSocket* socket = new ResumableUDPSocket(extension_->id());
  
    sockets_udp::SocketProperties* properties = params_.get()->properties.get();
    if (properties) {
      SetSocketProperties(socket, properties);
    }
  
    sockets_udp::CreateInfo create_info;
    create_info.socket_id = AddSocket(socket);
    results_ = sockets_udp::Create::Results::Create(create_info);
  }
  
  SocketsUdpUpdateFunction::SocketsUdpUpdateFunction() {}
  
  SocketsUdpUpdateFunction::~SocketsUdpUpdateFunction() {}
  
  bool SocketsUdpUpdateFunction::Prepare() {
    params_ = sockets_udp::Update::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpUpdateFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    SetSocketProperties(socket, &params_.get()->properties);
    results_ = sockets_udp::Update::Results::Create();
  }
  
  SocketsUdpSetPausedFunction::SocketsUdpSetPausedFunction()
      : socket_event_dispatcher_(NULL) {}
  
  SocketsUdpSetPausedFunction::~SocketsUdpSetPausedFunction() {}
  
  bool SocketsUdpSetPausedFunction::Prepare() {
    params_ = core_api::sockets_udp::SetPaused::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
  
    socket_event_dispatcher_ = UDPSocketEventDispatcher::Get(browser_context());
    DCHECK(socket_event_dispatcher_)
        << "There is no socket event dispatcher. "
           "If this assertion is failing during a test, then it is likely that "
           "TestExtensionSystem is failing to provide an instance of "
           "UDPSocketEventDispatcher.";
    return socket_event_dispatcher_ != NULL;
  }
  
  void SocketsUdpSetPausedFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    if (socket->paused() != params_->paused) {
      socket->set_paused(params_->paused);
      if (socket->IsBound() && !params_->paused) {
        socket_event_dispatcher_->OnSocketResume(extension_->id(),
                                                 params_->socket_id);
      }
    }
  
    results_ = sockets_udp::SetPaused::Results::Create();
  }
  
  SocketsUdpBindFunction::SocketsUdpBindFunction()
      : socket_event_dispatcher_(NULL) {}
  
  SocketsUdpBindFunction::~SocketsUdpBindFunction() {}
  
  bool SocketsUdpBindFunction::Prepare() {
    params_ = sockets_udp::Bind::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
  
    socket_event_dispatcher_ = UDPSocketEventDispatcher::Get(browser_context());
    DCHECK(socket_event_dispatcher_)
        << "There is no socket event dispatcher. "
           "If this assertion is failing during a test, then it is likely that "
           "TestExtensionSystem is failing to provide an instance of "
           "UDPSocketEventDispatcher.";
    return socket_event_dispatcher_ != NULL;
  }
  
  void SocketsUdpBindFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    content::SocketPermissionRequest param(
        SocketPermissionRequest::UDP_BIND, params_->address, params_->port);
    if (!SocketsManifestData::CheckRequest(extension(), param)) {
      error_ = kPermissionError;
      return;
    }
  
    int net_result = socket->Bind(params_->address, params_->port);
    if (net_result == net::OK) {
      socket_event_dispatcher_->OnSocketBind(extension_->id(),
                                             params_->socket_id);
    }
  
    if (net_result != net::OK)
      error_ = net::ErrorToString(net_result);
    results_ = sockets_udp::Bind::Results::Create(net_result);
  }
  
  SocketsUdpSendFunction::SocketsUdpSendFunction() : io_buffer_size_(0) {}
  
  SocketsUdpSendFunction::~SocketsUdpSendFunction() {}
  
  bool SocketsUdpSendFunction::Prepare() {
    params_ = sockets_udp::Send::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    io_buffer_size_ = params_->data.size();
    io_buffer_ = new net::WrappedIOBuffer(params_->data.data());
  
    return true;
  }
  
  void SocketsUdpSendFunction::AsyncWorkStart() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      AsyncWorkCompleted();
      return;
    }
  
    content::SocketPermissionRequest param(
        SocketPermissionRequest::UDP_SEND_TO, params_->address, params_->port);
    if (!SocketsManifestData::CheckRequest(extension(), param)) {
      error_ = kPermissionError;
      AsyncWorkCompleted();
      return;
    }
  
    StartDnsLookup(params_->address);
  }
  
  void SocketsUdpSendFunction::AfterDnsLookup(int lookup_result) {
    if (lookup_result == net::OK) {
      StartSendTo();
    } else {
      SetSendResult(lookup_result, -1);
    }
  }
  
  void SocketsUdpSendFunction::StartSendTo() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      AsyncWorkCompleted();
      return;
    }
  
    socket->SendTo(io_buffer_,
                   io_buffer_size_,
                   resolved_address_,
                   params_->port,
                   base::Bind(&SocketsUdpSendFunction::OnCompleted, this));
  }
  
  void SocketsUdpSendFunction::OnCompleted(int net_result) {
    if (net_result >= net::OK) {
      SetSendResult(net::OK, net_result);
    } else {
      SetSendResult(net_result, -1);
    }
  }
  
  void SocketsUdpSendFunction::SetSendResult(int net_result, int bytes_sent) {
    CHECK(net_result <= net::OK) << "Network status code must be < 0";
  
    sockets_udp::SendInfo send_info;
    send_info.result_code = net_result;
    if (net_result == net::OK) {
      send_info.bytes_sent.reset(new int(bytes_sent));
    }
  
    if (net_result != net::OK)
      error_ = net::ErrorToString(net_result);
    results_ = sockets_udp::Send::Results::Create(send_info);
    AsyncWorkCompleted();
  }
  
  SocketsUdpCloseFunction::SocketsUdpCloseFunction() {}
  
  SocketsUdpCloseFunction::~SocketsUdpCloseFunction() {}
  
  bool SocketsUdpCloseFunction::Prepare() {
    params_ = sockets_udp::Close::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpCloseFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    socket->Disconnect();
    RemoveSocket(params_->socket_id);
    results_ = sockets_udp::Close::Results::Create();
  }
  
  SocketsUdpGetInfoFunction::SocketsUdpGetInfoFunction() {}
  
  SocketsUdpGetInfoFunction::~SocketsUdpGetInfoFunction() {}
  
  bool SocketsUdpGetInfoFunction::Prepare() {
    params_ = sockets_udp::GetInfo::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpGetInfoFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    linked_ptr<sockets_udp::SocketInfo> socket_info =
        CreateSocketInfo(params_->socket_id, socket);
    results_ = sockets_udp::GetInfo::Results::Create(*socket_info);
  }
  
  SocketsUdpGetSocketsFunction::SocketsUdpGetSocketsFunction() {}
  
  SocketsUdpGetSocketsFunction::~SocketsUdpGetSocketsFunction() {}
  
  bool SocketsUdpGetSocketsFunction::Prepare() { return true; }
  
  void SocketsUdpGetSocketsFunction::Work() {
    std::vector<linked_ptr<sockets_udp::SocketInfo> > socket_infos;
    base::hash_set<int>* resource_ids = GetSocketIds();
    if (resource_ids != NULL) {
      for (base::hash_set<int>::iterator it = resource_ids->begin();
           it != resource_ids->end();
           ++it) {
        int socket_id = *it;
        ResumableUDPSocket* socket = GetUdpSocket(socket_id);
        if (socket) {
          socket_infos.push_back(CreateSocketInfo(socket_id, socket));
        }
      }
    }
    results_ = sockets_udp::GetSockets::Results::Create(socket_infos);
  }
  
  SocketsUdpJoinGroupFunction::SocketsUdpJoinGroupFunction() {}
  
  SocketsUdpJoinGroupFunction::~SocketsUdpJoinGroupFunction() {}
  
  bool SocketsUdpJoinGroupFunction::Prepare() {
    params_ = sockets_udp::JoinGroup::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpJoinGroupFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    content::SocketPermissionRequest param(
        SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
        kWildcardAddress,
        kWildcardPort);
    if (!SocketsManifestData::CheckRequest(extension(), param)) {
      error_ = kPermissionError;
      return;
    }
  
    int net_result = socket->JoinGroup(params_->address);
    if (net_result != net::OK)
      error_ = net::ErrorToString(net_result);
    results_ = sockets_udp::JoinGroup::Results::Create(net_result);
  }
  
  SocketsUdpLeaveGroupFunction::SocketsUdpLeaveGroupFunction() {}
  
  SocketsUdpLeaveGroupFunction::~SocketsUdpLeaveGroupFunction() {}
  
  bool SocketsUdpLeaveGroupFunction::Prepare() {
    params_ = core_api::sockets_udp::LeaveGroup::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpLeaveGroupFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    content::SocketPermissionRequest param(
        SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
        kWildcardAddress,
        kWildcardPort);
    if (!SocketsManifestData::CheckRequest(extension(), param)) {
      error_ = kPermissionError;
      return;
    }
  
    int net_result = socket->LeaveGroup(params_->address);
    if (net_result != net::OK)
      error_ = net::ErrorToString(net_result);
    results_ = sockets_udp::LeaveGroup::Results::Create(net_result);
  }
  
  SocketsUdpSetMulticastTimeToLiveFunction::
      SocketsUdpSetMulticastTimeToLiveFunction() {}
  
  SocketsUdpSetMulticastTimeToLiveFunction::
      ~SocketsUdpSetMulticastTimeToLiveFunction() {}
  
  bool SocketsUdpSetMulticastTimeToLiveFunction::Prepare() {
    params_ =
        core_api::sockets_udp::SetMulticastTimeToLive::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpSetMulticastTimeToLiveFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    int net_result = socket->SetMulticastTimeToLive(params_->ttl);
    if (net_result != net::OK)
      error_ = net::ErrorToString(net_result);
    results_ = sockets_udp::SetMulticastTimeToLive::Results::Create(net_result);
  }
  
  SocketsUdpSetMulticastLoopbackModeFunction::
      SocketsUdpSetMulticastLoopbackModeFunction() {}
  
  SocketsUdpSetMulticastLoopbackModeFunction::
      ~SocketsUdpSetMulticastLoopbackModeFunction() {}
  
  bool SocketsUdpSetMulticastLoopbackModeFunction::Prepare() {
    params_ =
        core_api::sockets_udp::SetMulticastLoopbackMode::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpSetMulticastLoopbackModeFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    int net_result = socket->SetMulticastLoopbackMode(params_->enabled);
    if (net_result != net::OK)
      error_ = net::ErrorToString(net_result);
    results_ = sockets_udp::SetMulticastLoopbackMode::Results::Create(net_result);
  }
  
  SocketsUdpGetJoinedGroupsFunction::SocketsUdpGetJoinedGroupsFunction() {}
  
  SocketsUdpGetJoinedGroupsFunction::~SocketsUdpGetJoinedGroupsFunction() {}
  
  bool SocketsUdpGetJoinedGroupsFunction::Prepare() {
    params_ = core_api::sockets_udp::GetJoinedGroups::Params::Create(*args_);
    EXTENSION_FUNCTION_VALIDATE(params_.get());
    return true;
  }
  
  void SocketsUdpGetJoinedGroupsFunction::Work() {
    ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
    if (!socket) {
      error_ = kSocketNotFoundError;
      return;
    }
  
    content::SocketPermissionRequest param(
        SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
        kWildcardAddress,
        kWildcardPort);
    if (!SocketsManifestData::CheckRequest(extension(), param)) {
      error_ = kPermissionError;
      return;
    }
  
    const std::vector<std::string>& groups = socket->GetJoinedGroups();
    results_ = sockets_udp::GetJoinedGroups::Results::Create(groups);
  }
  
  }  // namespace core_api
  }  // namespace extensions
  