// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/network_context_client_base_impl.h"

#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "components/file_access/scoped_file_access.h"
#include "content/browser/security/cpsp/child_process_security_policy_impl.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/network_context_client_base.h"
#include "content/public/common/child_process_id.h"
#include "content/public/common/child_process_id_util.h"
#include "content/public/common/content_client.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/net_errors.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"

namespace content {

namespace {

void HandleFileUploadRequest(
    network::OriginatingProcessId process_id,
    bool async,
    const std::vector<base::FilePath>& file_paths,
    network::mojom::NetworkContextClient::OnFileUploadRequestedCallback
        callback,
    scoped_refptr<base::TaskRunner> task_runner,
    file_access::ScopedFileAccess scoped_file_access) {
  std::vector<base::File> files;
  uint32_t file_flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
                        (async ? base::File::FLAG_ASYNC : 0);
  ChildProcessSecurityPolicyImpl* cpsp =
      ChildProcessSecurityPolicyImpl::GetInstance();
  for (const auto& file_path : file_paths) {
    bool access_denied = false;
    if (base::FeatureList::IsEnabled(
            network::features::kBrowserInitiatedFileUploadValidation) &&
        process_id.is_browser()) {
      access_denied = !cpsp->CanReadFileForBrowserUpload(file_path);
    } else if (!process_id.is_browser()) {
      access_denied = !cpsp->CanReadFile(
          ToChildProcessId(process_id.renderer_process_id()), file_path);
    }

    if (access_denied) {
      task_runner->PostTask(
          FROM_HERE, base::BindOnce(std::move(callback), net::ERR_ACCESS_DENIED,
                                    std::vector<base::File>()));
      return;
    }
    files.emplace_back(file_path, file_flags);
    if (!files.back().IsValid()) {
      task_runner->PostTask(
          FROM_HERE,
          base::BindOnce(std::move(callback),
                         net::FileErrorToNetError(files.back().error_details()),
                         std::vector<base::File>()));
      return;
    }
  }
  task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback), net::OK,
                                                  std::move(files)));
}

}  // namespace

void OnScopedFilesAccessAcquired(
    network::OriginatingProcessId process_id,
    bool async,
    const std::vector<base::FilePath>& file_paths,
    network::mojom::NetworkContextClient::OnFileUploadRequestedCallback
        callback,
    file_access::ScopedFileAccess scoped_file_access) {
  if (!scoped_file_access.is_allowed()) {
    std::move(callback).Run(net::Error::ERR_ACCESS_DENIED, /*files=*/{});
    return;
  }
  base::ThreadPool::PostTask(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
      base::BindOnce(&HandleFileUploadRequest, process_id, async, file_paths,
                     std::move(callback),
                     base::SequencedTaskRunner::GetCurrentDefault(),
                     std::move(scoped_file_access)));
}

void NetworkContextOnFileUploadRequested(
    network::OriginatingProcessId process_id,
    bool async,
    const std::vector<base::FilePath>& file_paths,
    const GURL& destination_url,
    network::mojom::NetworkContextClient::OnFileUploadRequestedCallback
        callback) {
  GetContentClient()->browser()->RequestFilesAccess(
      file_paths, destination_url,
      base::BindOnce(&OnScopedFilesAccessAcquired, process_id, async,
                     file_paths, std::move(callback)));
}

NetworkContextClientBase::NetworkContextClientBase() = default;
NetworkContextClientBase::~NetworkContextClientBase() = default;

void NetworkContextClientBase::OnFileUploadRequested(
    const network::OriginatingProcessId& process_id,
    bool async,
    const std::vector<base::FilePath>& file_paths,
    const GURL& destination_url,
    OnFileUploadRequestedCallback callback) {
  NetworkContextOnFileUploadRequested(process_id, async, file_paths,
                                      destination_url, std::move(callback));
}

void NetworkContextClientBase::OnCanSendReportingReports(
    const std::vector<url::Origin>& origins,
    OnCanSendReportingReportsCallback callback) {
  std::move(callback).Run(std::vector<url::Origin>());
}

void NetworkContextClientBase::OnCanSendDomainReliabilityUpload(
    const url::Origin& origin,
    OnCanSendDomainReliabilityUploadCallback callback) {
  std::move(callback).Run(false);
}

#if BUILDFLAG(IS_ANDROID)
void NetworkContextClientBase::OnGenerateHttpNegotiateAuthToken(
    const std::string& server_auth_token,
    bool can_delegate,
    const std::string& auth_negotiate_android_account_type,
    const std::string& spn,
    OnGenerateHttpNegotiateAuthTokenCallback callback) {
  std::move(callback).Run(net::ERR_FAILED, server_auth_token);
}
#endif

#if BUILDFLAG(IS_CT_SUPPORTED)
void NetworkContextClientBase::OnCanSendSCTAuditingReport(
    OnCanSendSCTAuditingReportCallback callback) {
  std::move(callback).Run(false);
}

void NetworkContextClientBase::OnNewSCTAuditingReportSent() {}
#endif

}  // namespace content
