// Copyright 2015 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/privet_v3_context_getter.h"

#include "base/command_line.h"
#include "base/macros.h"
#include "chrome/common/chrome_content_client.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/x509_certificate.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"

namespace extensions {

// Class verifies certificate by its fingerprint received using different
// channel. It's the only know information about device with self-signed
// certificate.
class PrivetV3ContextGetter::CertVerifier : public net::CertVerifier {
 public:
  CertVerifier() {}

  int Verify(net::X509Certificate* cert,
             const std::string& hostname,
             const std::string& ocsp_response,
             int flags,
             net::CRLSet* crl_set,
             net::CertVerifyResult* verify_result,
             const net::CompletionCallback& callback,
             scoped_ptr<Request>* out_req,
             const net::BoundNetLog& net_log) override {
    verify_result->Reset();
    verify_result->verified_cert = cert;

    // Because no trust anchor checking is being performed, don't indicate that
    // it came from an OS-trusted root.
    verify_result->is_issued_by_known_root = false;
    // Because no trust anchor checking is being performed, don't indicate that
    // it came from a supplemental trust anchor.
    verify_result->is_issued_by_additional_trust_anchor = false;
    // Because no name checking is being performed, don't indicate that it the
    // common name was used.
    verify_result->common_name_fallback_used = false;
    // Because the signature is not checked, do not indicate any deprecated
    // signature algorithms were used, even if they might be present.
    verify_result->has_md2 = false;
    verify_result->has_md4 = false;
    verify_result->has_md5 = false;
    verify_result->has_sha1 = false;
    verify_result->has_sha1_leaf = false;
    // Because no chain hashes calculation is being performed, keep hashes
    // container clean.
    verify_result->public_key_hashes.clear();

    verify_result->cert_status = CheckFingerprint(cert, hostname)
                                     ? 0
                                     : net::CERT_STATUS_AUTHORITY_INVALID;
    return net::IsCertStatusError(verify_result->cert_status)
               ? net::MapCertStatusToNetError(verify_result->cert_status)
               : net::OK;
  }

  void AddPairedHost(const std::string& host,
                     const net::SHA256HashValue& certificate_fingerprint) {
    fingerprints_[host] = certificate_fingerprint;
  }

 private:
  bool CheckFingerprint(net::X509Certificate* cert,
                        const std::string& hostname) const {
    auto it = fingerprints_.find(hostname);
    if (it == fingerprints_.end())
      return false;

    return it->second.Equals(
        net::X509Certificate::CalculateFingerprint256(cert->os_cert_handle()));
  }

  std::map<std::string, net::SHA256HashValue> fingerprints_;

  DISALLOW_COPY_AND_ASSIGN(CertVerifier);
};

PrivetV3ContextGetter::PrivetV3ContextGetter(
    const scoped_refptr<base::SingleThreadTaskRunner>& net_task_runner)
    : net_task_runner_(net_task_runner), weak_ptr_factory_(this) {
}

net::URLRequestContext* PrivetV3ContextGetter::GetURLRequestContext() {
  InitOnNetThread();
  return context_.get();
}

scoped_refptr<base::SingleThreadTaskRunner>
PrivetV3ContextGetter::GetNetworkTaskRunner() const {
  return net_task_runner_;
}

void PrivetV3ContextGetter::InitOnNetThread() {
  DCHECK(net_task_runner_->BelongsToCurrentThread());
  if (!context_) {
    net::URLRequestContextBuilder builder;

    builder.set_proxy_service(net::ProxyService::CreateDirect());
    builder.SetSpdyAndQuicEnabled(false, false);
    builder.DisableHttpCache();
    cert_verifier_ = new CertVerifier();
    builder.SetCertVerifier(make_scoped_ptr(cert_verifier_));
    builder.set_user_agent(::GetUserAgent());
    context_ = builder.Build();
  }
}

void PrivetV3ContextGetter::AddPairedHost(
    const std::string& host,
    const net::SHA256HashValue& certificate_fingerprint,
    const base::Closure& callback) {
  net_task_runner_->PostTaskAndReply(
      FROM_HERE,
      base::Bind(&PrivetV3ContextGetter::AddPairedHostOnNetThread,
                 weak_ptr_factory_.GetWeakPtr(), host, certificate_fingerprint),
      callback);
}

void PrivetV3ContextGetter::AddPairedHostOnNetThread(
    const std::string& host,
    const net::SHA256HashValue& certificate_fingerprint) {
  InitOnNetThread();
  cert_verifier_->AddPairedHost(host, certificate_fingerprint);
}

PrivetV3ContextGetter::~PrivetV3ContextGetter() {
  DCHECK(net_task_runner_->BelongsToCurrentThread());
}

}  // namespace extensions