// Copyright 2018 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 "net/cookies/cookie_deletion_info.h"

#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cookies/cookie_options.h"

namespace net {

namespace {

// Return true if the eTLD+1 of the cookies domain matches any of the strings
// in |match_domains|, false otherwise.
bool DomainMatchesDomains(const net::CanonicalCookie& cookie,
                          const std::set<std::string>& match_domains) {
  if (match_domains.empty())
    return false;

  // If domain is an IP address it returns an empty string.
  std::string effective_domain(
      net::registry_controlled_domains::GetDomainAndRegistry(
          // GetDomainAndRegistry() is insensitive to leading dots, i.e.
          // to host/domain cookie distinctions.
          cookie.Domain(),
          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
  // If the cookie's domain is is not parsed as belonging to a registry
  // (e.g. for IP addresses or internal hostnames) an empty string will be
  // returned.  In this case, use the domain in the cookie.
  if (effective_domain.empty()) {
    if (cookie.IsDomainCookie())
      effective_domain = cookie.Domain().substr(1);
    else
      effective_domain = cookie.Domain();
  }

  return match_domains.count(effective_domain) != 0;
}

}  // anonymous namespace

CookieDeletionInfo::TimeRange::TimeRange() = default;

CookieDeletionInfo::TimeRange::TimeRange(const TimeRange& other) = default;

CookieDeletionInfo::TimeRange::TimeRange(base::Time start, base::Time end)
    : start_(start), end_(end) {
  if (!start.is_null() && !end.is_null())
    DCHECK_GE(end, start);
}

CookieDeletionInfo::TimeRange& CookieDeletionInfo::TimeRange::operator=(
    const TimeRange& rhs) = default;

bool CookieDeletionInfo::TimeRange::Contains(const base::Time& time) const {
  DCHECK(!time.is_null());

  if (!start_.is_null() && start_ == end_)
    return time == start_;
  return (start_.is_null() || start_ <= time) &&
         (end_.is_null() || time < end_);
}

void CookieDeletionInfo::TimeRange::SetStart(base::Time value) {
  start_ = value;
}

void CookieDeletionInfo::TimeRange::SetEnd(base::Time value) {
  end_ = value;
}

CookieDeletionInfo::CookieDeletionInfo()
    : CookieDeletionInfo(base::Time(), base::Time()) {}

CookieDeletionInfo::CookieDeletionInfo(base::Time start_time,
                                       base::Time end_time)
    : creation_range(start_time, end_time) {
  // Options to use for deletion of cookies associated with
  // a particular URL.  These options will make sure that all
  // cookies associated with the URL are deleted.
  cookie_options.set_include_httponly();
  cookie_options.set_same_site_cookie_mode(
      net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
}

CookieDeletionInfo::CookieDeletionInfo(CookieDeletionInfo&& other) = default;

CookieDeletionInfo::CookieDeletionInfo(const CookieDeletionInfo& other) =
    default;

CookieDeletionInfo::~CookieDeletionInfo() = default;

CookieDeletionInfo& CookieDeletionInfo::operator=(CookieDeletionInfo&& rhs) =
    default;

CookieDeletionInfo& CookieDeletionInfo::operator=(
    const CookieDeletionInfo& rhs) = default;

bool CookieDeletionInfo::Matches(const CanonicalCookie& cookie) const {
  if (session_control != SessionControl::IGNORE_CONTROL &&
      (cookie.IsPersistent() !=
       (session_control == SessionControl::PERSISTENT_COOKIES))) {
    return false;
  }

  if (!creation_range.Contains(cookie.CreationDate()))
    return false;

  if (host.has_value() &&
      !(cookie.IsHostCookie() && cookie.IsDomainMatch(host.value()))) {
    return false;
  }

  if (name.has_value() && cookie.Name() != name)
    return false;

  if (value_for_testing.has_value() &&
      value_for_testing.value() != cookie.Value()) {
    return false;
  }

  if (url.has_value() &&
      cookie.IncludeForRequestURL(url.value(), cookie_options) !=
          CanonicalCookie::CookieInclusionStatus::INCLUDE) {
    return false;
  }

  if (!domains_and_ips_to_delete.empty() &&
      !DomainMatchesDomains(cookie, domains_and_ips_to_delete)) {
    return false;
  }

  if (!domains_and_ips_to_ignore.empty() &&
      DomainMatchesDomains(cookie, domains_and_ips_to_ignore)) {
    return false;
  }

  return true;
}

}  // namespace net
