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

#import "ios/components/cookie_util/cookie_util.h"

#import <Foundation/Foundation.h>
#import <stddef.h>
#import <stdint.h>
#import <sys/sysctl.h>

#import "base/check.h"
#import "base/functional/bind.h"
#import "base/functional/callback_helpers.h"
#import "base/memory/ref_counted.h"
#import "base/task/thread_pool.h"
#import "components/prefs/pref_service.h"
#import "ios/components/cookie_util/cookie_constants.h"
#import "ios/net/cookies/cookie_store_ios.h"
#import "ios/net/cookies/system_cookie_store.h"
#import "ios/web/common/features.h"
#import "ios/web/public/browser_state.h"
#import "ios/web/public/thread/web_task_traits.h"
#import "ios/web/public/thread/web_thread.h"
#import "net/cookies/cookie_monster.h"
#import "net/cookies/cookie_store.h"
#import "net/extras/sqlite/sqlite_persistent_cookie_store.h"
#import "net/log/net_log.h"
#import "net/url_request/url_request_context.h"
#import "net/url_request/url_request_context_getter.h"

namespace cookie_util {

namespace {

// Creates a SQLitePersistentCookieStore running on a background thread.
scoped_refptr<net::SQLitePersistentCookieStore> CreatePersistentCookieStore(
    const base::FilePath& path,
    bool restore_old_session_cookies) {
  return scoped_refptr<net::SQLitePersistentCookieStore>(
      new net::SQLitePersistentCookieStore(
          path, web::GetIOThreadTaskRunner({}),
          base::ThreadPool::CreateSequencedTaskRunner(
              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
               base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
          restore_old_session_cookies, /*crypto_delegate=*/nullptr,
          /*enable_exclusive_access=*/false));
}

// Creates a CookieMonster configured by `config`.
std::unique_ptr<net::CookieMonster> CreateCookieMonster(
    const CookieStoreConfig& config,
    net::NetLog* net_log) {
  if (config.path.empty()) {
    // Empty path means in-memory store.
    return std::make_unique<net::CookieMonster>(nullptr /* store */, net_log);
  }

  const bool restore_old_session_cookies =
      config.session_cookie_mode == CookieStoreConfig::RESTORED_SESSION_COOKIES;
  scoped_refptr<net::SQLitePersistentCookieStore> persistent_store =
      CreatePersistentCookieStore(config.path, restore_old_session_cookies);
  std::unique_ptr<net::CookieMonster> cookie_monster(
      new net::CookieMonster(persistent_store.get(), net_log));
  if (restore_old_session_cookies) {
    cookie_monster->SetPersistSessionCookies(true);
  }
  return cookie_monster;
}

}  // namespace

CookieStoreConfig::CookieStoreConfig(const base::FilePath& path,
                                     SessionCookieMode session_cookie_mode,
                                     CookieStoreType cookie_store_type)
    : path(path),
      session_cookie_mode(session_cookie_mode),
      cookie_store_type(cookie_store_type) {
  CHECK(!path.empty() || session_cookie_mode == EPHEMERAL_SESSION_COOKIES);
}

CookieStoreConfig::~CookieStoreConfig() {}

std::unique_ptr<net::CookieStore> CreateCookieStore(
    const CookieStoreConfig& config,
    std::unique_ptr<net::SystemCookieStore> system_cookie_store,
    net::NetLog* net_log) {
  if (config.cookie_store_type == CookieStoreConfig::COOKIE_MONSTER) {
    return CreateCookieMonster(config, net_log);
  }

  // Using the SystemCookieStore will allow URLFetcher and any other users of
  // net:CookieStore to in iOS to use cookies directly from WKHTTPCookieStore.
  return std::make_unique<net::CookieStoreIOS>(std::move(system_cookie_store),
                                               net_log);
}

bool ShouldClearSessionCookies(PrefService* pref_service) {
  const base::Time last_cookie_deletion_date =
      pref_service->GetTime(kLastCookieDeletionDate);

  bool clear_cookies = true;
  if (!last_cookie_deletion_date.is_null()) {
    struct timeval boottime;
    int mib[2] = {CTL_KERN, KERN_BOOTTIME};
    size_t size = sizeof(boottime);

    if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1) {
      if (boottime.tv_sec != 0) {
        const base::Time boot = base::Time::FromTimeVal(boottime);

        clear_cookies = boot > last_cookie_deletion_date;
      }
    }
  }
  if (clear_cookies) {
    pref_service->SetTime(kLastCookieDeletionDate, base::Time::Now());
  }
  return clear_cookies;
}

// Clears the session cookies for `browser_state`.
void ClearSessionCookies(web::BrowserState* browser_state) {
  scoped_refptr<net::URLRequestContextGetter> getter =
      browser_state->GetRequestContext();
  web::GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(^{
        net::CookieStore* cookie_store =
            getter->GetURLRequestContext()->cookie_store();
        if (cookie_store) {
          cookie_store->DeleteSessionCookiesAsync(base::DoNothing());
        }
      }));
}

}  // namespace cookie_util
