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 "chrome/browser/extensions/api/gcd_private/gcd_private_api.h"
  
  #include <memory>
  
  #include "base/lazy_instance.h"
  #include "base/macros.h"
  #include "base/threading/thread_task_runner_handle.h"
  #include "chrome/browser/extensions/api/gcd_private/privet_v3_context_getter.h"
  #include "chrome/browser/extensions/api/gcd_private/privet_v3_session.h"
  #include "chrome/browser/local_discovery/endpoint_resolver.h"
  #include "chrome/browser/local_discovery/service_discovery_shared_client.h"
  #include "chrome/browser/profiles/profile.h"
  #include "content/public/browser/browser_context.h"
  #include "content/public/browser/storage_partition.h"
  #include "net/url_request/url_request_context_getter.h"
  
  namespace extensions {
  
  namespace gcd_private = api::gcd_private;
  
  namespace {
  
  const char kPrivatAPISetup[] = "/privet/v3/setup/start";
  const char kPrivetKeyWifi[] = "wifi";
  const char kPrivetKeyPassphrase[] = "passphrase";
  const char kPrivetKeySSID[] = "ssid";
  
  base::LazyInstance<BrowserContextKeyedAPIFactory<GcdPrivateAPI> > g_factory =
      LAZY_INSTANCE_INITIALIZER;
  
  }  // namespace
  
  class GcdPrivateSessionHolder;
  
  class GcdPrivateAPIImpl {
   public:
    typedef base::Callback<void(bool success)> SuccessCallback;
  
    typedef base::Callback<void(int session_id,
                                api::gcd_private::Status status,
                                const base::DictionaryValue& info)>
        CreateSessionCallback;
  
    typedef base::Callback<void(api::gcd_private::Status status)> SessionCallback;
  
    typedef base::Callback<void(api::gcd_private::Status status,
                                const base::DictionaryValue& response)>
        MessageResponseCallback;
  
    explicit GcdPrivateAPIImpl(content::BrowserContext* context);
    virtual ~GcdPrivateAPIImpl();
  
    static GcdPrivateAPIImpl* Get(content::BrowserContext* context);
  
    void CreateSession(const std::string& service_name,
                       const CreateSessionCallback& callback);
  
    void StartPairing(int session_id,
                      api::gcd_private::PairingType pairing_type,
                      const SessionCallback& callback);
  
    void ConfirmCode(int session_id,
                     const std::string& code,
                     const SessionCallback& callback);
  
    void SendMessage(int session_id,
                     const std::string& api,
                     const base::DictionaryValue& input,
                     const MessageResponseCallback& callback);
  
    void RemoveSession(int session_id);
    void RemoveSessionDelayed(int session_id);
  
    std::unique_ptr<base::ListValue> GetPrefetchedSSIDList();
  
   private:
    typedef std::map<std::string /* ssid */, std::string /* password */>
        PasswordMap;
  
    void SendMessageInternal(int session_id,
                             const std::string& api,
                             const base::DictionaryValue& input,
                             const MessageResponseCallback& callback);
  
    void OnServiceResolved(int session_id,
                           const CreateSessionCallback& callback,
                           const net::IPEndPoint& endpoint);
  
    scoped_refptr<local_discovery::ServiceDiscoverySharedClient>
        service_discovery_client_;
  
    struct SessionInfo {
      std::unique_ptr<PrivetV3Session> session;
      std::unique_ptr<local_discovery::EndpointResolver> resolver;
    };
  
    std::map<int, SessionInfo> sessions_;
    int last_session_id_ = 0;
  
    content::BrowserContext* const browser_context_;
  
    scoped_refptr<PrivetV3ContextGetter> context_getter_;
  
    base::WeakPtrFactory<GcdPrivateAPIImpl> weak_ptr_factory_{this};
  
    DISALLOW_COPY_AND_ASSIGN(GcdPrivateAPIImpl);
  };
  
  GcdPrivateAPIImpl::GcdPrivateAPIImpl(content::BrowserContext* context)
      : browser_context_(context) {
    DCHECK(browser_context_);
  }
  
  GcdPrivateAPIImpl::~GcdPrivateAPIImpl() {
  }
  
  // static
  GcdPrivateAPIImpl* GcdPrivateAPIImpl::Get(content::BrowserContext* context) {
    GcdPrivateAPI* gcd_api =
        BrowserContextKeyedAPIFactory<GcdPrivateAPI>::Get(context);
    return gcd_api ? gcd_api->impl_.get() : NULL;
  }
  
  void GcdPrivateAPIImpl::CreateSession(const std::string& service_name,
                                        const CreateSessionCallback& callback) {
    int session_id = last_session_id_++;
    auto& session_data = sessions_[session_id];
    session_data.resolver.reset(new local_discovery::EndpointResolver());
    session_data.resolver->Start(
        service_name, base::Bind(&GcdPrivateAPIImpl::OnServiceResolved,
                                 base::Unretained(this), session_id, callback));
  }
  
  void GcdPrivateAPIImpl::OnServiceResolved(int session_id,
                                            const CreateSessionCallback& callback,
                                            const net::IPEndPoint& endpoint) {
    if (endpoint.address().empty()) {
      return callback.Run(session_id, gcd_private::STATUS_SERVICERESOLUTIONERROR,
                          base::DictionaryValue());
    }
    auto& session_data = sessions_[session_id];
  
    if (!context_getter_) {
      context_getter_ = new PrivetV3ContextGetter(
          content::BrowserContext::GetDefaultStoragePartition(browser_context_)->
              GetURLRequestContext()->GetNetworkTaskRunner());
    }
  
    session_data.session.reset(new PrivetV3Session(
        context_getter_, net::HostPortPair::FromIPEndPoint(endpoint)));
    session_data.session->Init(base::Bind(callback, session_id));
  }
  
  void GcdPrivateAPIImpl::StartPairing(int session_id,
                                       api::gcd_private::PairingType pairing_type,
                                       const SessionCallback& callback) {
    auto found = sessions_.find(session_id);
  
    if (found == sessions_.end())
      return callback.Run(gcd_private::STATUS_UNKNOWNSESSIONERROR);
  
    found->second.session->StartPairing(pairing_type, callback);
  }
  
  void GcdPrivateAPIImpl::ConfirmCode(int session_id,
                                      const std::string& code,
                                      const SessionCallback& callback) {
    auto found = sessions_.find(session_id);
  
    if (found == sessions_.end())
      return callback.Run(gcd_private::STATUS_UNKNOWNSESSIONERROR);
  
    found->second.session->ConfirmCode(code, callback);
  }
  
  void GcdPrivateAPIImpl::SendMessage(int session_id,
                                      const std::string& api,
                                      const base::DictionaryValue& input,
                                      const MessageResponseCallback& callback) {
    const base::DictionaryValue* input_actual = &input;
    std::unique_ptr<base::DictionaryValue> input_cloned;
  
    if (api == kPrivatAPISetup) {
      const base::DictionaryValue* wifi = NULL;
  
      if (input.GetDictionary(kPrivetKeyWifi, &wifi)) {
        std::string ssid;
  
        if (!wifi->GetString(kPrivetKeySSID, &ssid)) {
          LOG(ERROR) << "Missing " << kPrivetKeySSID;
          return callback.Run(gcd_private::STATUS_SETUPPARSEERROR,
                              base::DictionaryValue());
        }
  
        if (!wifi->HasKey(kPrivetKeyPassphrase)) {
          LOG(ERROR) << "Password is unknown";
          return callback.Run(gcd_private::STATUS_WIFIPASSWORDERROR,
                              base::DictionaryValue());
        }
      }
    }
  
    auto found = sessions_.find(session_id);
  
    if (found == sessions_.end()) {
      return callback.Run(gcd_private::STATUS_UNKNOWNSESSIONERROR,
                          base::DictionaryValue());
    }
  
    found->second.session->SendMessage(api, *input_actual, callback);
  }
  
  void GcdPrivateAPIImpl::RemoveSession(int session_id) {
    sessions_.erase(session_id);
  }
  
  void GcdPrivateAPIImpl::RemoveSessionDelayed(int session_id) {
    base::ThreadTaskRunnerHandle::Get()->PostTask(
        FROM_HERE, base::Bind(&GcdPrivateAPIImpl::RemoveSession,
                              weak_ptr_factory_.GetWeakPtr(), session_id));
  }
  
  std::unique_ptr<base::ListValue> GcdPrivateAPIImpl::GetPrefetchedSSIDList() {
    std::unique_ptr<base::ListValue> retval(new base::ListValue);
  
    return retval;
  }
  
  
  
  GcdPrivateAPI::GcdPrivateAPI(content::BrowserContext* context)
      : impl_(new GcdPrivateAPIImpl(context)) {
  }
  
  GcdPrivateAPI::~GcdPrivateAPI() {
  }
  
  // static
  BrowserContextKeyedAPIFactory<GcdPrivateAPI>*
  GcdPrivateAPI::GetFactoryInstance() {
    return g_factory.Pointer();
  }
  
  GcdPrivateGetDeviceInfoFunction::GcdPrivateGetDeviceInfoFunction() {
  }
  
  GcdPrivateGetDeviceInfoFunction::~GcdPrivateGetDeviceInfoFunction() {
  }
  
  bool GcdPrivateGetDeviceInfoFunction::RunAsync() {
    std::unique_ptr<gcd_private::CreateSession::Params> params =
        gcd_private::CreateSession::Params::Create(*args_);
  
    if (!params)
      return false;
  
    GcdPrivateAPIImpl* gcd_api = GcdPrivateAPIImpl::Get(GetProfile());
  
    GcdPrivateAPIImpl::CreateSessionCallback callback =
        base::Bind(&GcdPrivateGetDeviceInfoFunction::OnSessionInitialized, this);
    gcd_api->CreateSession(params->service_name, callback);
  
    return true;
  }
  
  void GcdPrivateGetDeviceInfoFunction::OnSessionInitialized(
      int session_id,
      api::gcd_private::Status status,
      const base::DictionaryValue& info) {
    gcd_private::GetDeviceInfo::Results::DeviceInfo device_info;
    device_info.additional_properties.MergeDictionary(&info);
  
    results_ = gcd_private::GetDeviceInfo::Results::Create(status, device_info);
    SendResponse(true);
  
    GcdPrivateAPIImpl* gcd_api = GcdPrivateAPIImpl::Get(GetProfile());
    gcd_api->RemoveSessionDelayed(session_id);
  }
  
  GcdPrivateCreateSessionFunction::GcdPrivateCreateSessionFunction() {
  }
  
  GcdPrivateCreateSessionFunction::~GcdPrivateCreateSessionFunction() {
  }
  
  bool GcdPrivateCreateSessionFunction::RunAsync() {
    std::unique_ptr<gcd_private::CreateSession::Params> params =
        gcd_private::CreateSession::Params::Create(*args_);
  
    if (!params)
      return false;
  
    GcdPrivateAPIImpl* gcd_api = GcdPrivateAPIImpl::Get(GetProfile());
  
    GcdPrivateAPIImpl::CreateSessionCallback callback =
        base::Bind(&GcdPrivateCreateSessionFunction::OnSessionInitialized, this);
    gcd_api->CreateSession(params->service_name, callback);
  
    return true;
  }
  
  void GcdPrivateCreateSessionFunction::OnSessionInitialized(
      int session_id,
      api::gcd_private::Status status,
      const base::DictionaryValue& info) {
    std::vector<api::gcd_private::PairingType> pairing_types;
  
    // TODO(vitalybuka): Remove this parsing and |pairing_types| from callback.
    if (status == gcd_private::STATUS_SUCCESS) {
      const base::ListValue* pairing = nullptr;
      if (info.GetList("authentication.pairing", &pairing)) {
        for (const auto& value : *pairing) {
          std::string pairing_string;
          if (value->GetAsString(&pairing_string)) {
            api::gcd_private::PairingType pairing_type =
                api::gcd_private::ParsePairingType(pairing_string);
            if (pairing_type != api::gcd_private::PAIRING_TYPE_NONE)
              pairing_types.push_back(pairing_type);
          }
        }
      } else {
        status = gcd_private::STATUS_SESSIONERROR;
      }
    }
  
    results_ = gcd_private::CreateSession::Results::Create(session_id, status,
                                                           pairing_types);
    SendResponse(true);
  }
  
  GcdPrivateStartPairingFunction::GcdPrivateStartPairingFunction() {
  }
  
  GcdPrivateStartPairingFunction::~GcdPrivateStartPairingFunction() {
  }
  
  bool GcdPrivateStartPairingFunction::RunAsync() {
    std::unique_ptr<gcd_private::StartPairing::Params> params =
        gcd_private::StartPairing::Params::Create(*args_);
  
    if (!params)
      return false;
  
    GcdPrivateAPIImpl* gcd_api = GcdPrivateAPIImpl::Get(GetProfile());
  
    gcd_api->StartPairing(
        params->session_id, params->pairing_type,
        base::Bind(&GcdPrivateStartPairingFunction::OnPairingStarted, this));
  
    return true;
  }
  
  void GcdPrivateStartPairingFunction::OnPairingStarted(
      api::gcd_private::Status status) {
    results_ = gcd_private::StartPairing::Results::Create(status);
    SendResponse(true);
  }
  
  GcdPrivateConfirmCodeFunction::GcdPrivateConfirmCodeFunction() {
  }
  
  GcdPrivateConfirmCodeFunction::~GcdPrivateConfirmCodeFunction() {
  }
  
  bool GcdPrivateConfirmCodeFunction::RunAsync() {
    std::unique_ptr<gcd_private::ConfirmCode::Params> params =
        gcd_private::ConfirmCode::Params::Create(*args_);
  
    if (!params)
      return false;
  
    GcdPrivateAPIImpl* gcd_api = GcdPrivateAPIImpl::Get(GetProfile());
  
    gcd_api->ConfirmCode(
        params->session_id, params->code,
        base::Bind(&GcdPrivateConfirmCodeFunction::OnCodeConfirmed, this));
  
    return true;
  }
  
  void GcdPrivateConfirmCodeFunction::OnCodeConfirmed(
      api::gcd_private::Status status) {
    results_ = gcd_private::ConfirmCode::Results::Create(status);
    SendResponse(true);
  }
  
  GcdPrivateSendMessageFunction::GcdPrivateSendMessageFunction() {
  }
  
  GcdPrivateSendMessageFunction::~GcdPrivateSendMessageFunction() {
  }
  
  bool GcdPrivateSendMessageFunction::RunAsync() {
    std::unique_ptr<gcd_private::PassMessage::Params> params =
        gcd_private::PassMessage::Params::Create(*args_);
  
    if (!params)
      return false;
  
    GcdPrivateAPIImpl* gcd_api = GcdPrivateAPIImpl::Get(GetProfile());
  
  
    gcd_api->SendMessage(
        params->session_id,
        params->api,
        params->input.additional_properties,
        base::Bind(&GcdPrivateSendMessageFunction::OnMessageSentCallback, this));
  
    return true;
  }
  
  void GcdPrivateSendMessageFunction::OnMessageSentCallback(
      api::gcd_private::Status status,
      const base::DictionaryValue& value) {
    gcd_private::PassMessage::Results::Response response;
    response.additional_properties.MergeDictionary(&value);
  
    results_ = gcd_private::PassMessage::Results::Create(status, response);
    SendResponse(true);
  }
  
  GcdPrivateTerminateSessionFunction::GcdPrivateTerminateSessionFunction() {
  }
  
  GcdPrivateTerminateSessionFunction::~GcdPrivateTerminateSessionFunction() {
  }
  
  bool GcdPrivateTerminateSessionFunction::RunAsync() {
    std::unique_ptr<gcd_private::TerminateSession::Params> params =
        gcd_private::TerminateSession::Params::Create(*args_);
  
    if (!params)
      return false;
  
    GcdPrivateAPIImpl* gcd_api = GcdPrivateAPIImpl::Get(GetProfile());
  
    gcd_api->RemoveSession(params->session_id);
  
    SendResponse(true);
    return true;
  }
  
  }  // namespace extensions
  