blob: 60b3ca6000fe6d1df9cb1f12933ecfe92663d857 [file] [log] [blame]
// 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 "ios/net/cookies/system_cookie_util.h"
#import <Foundation/Foundation.h>
#include <stddef.h>
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/sys_string_conversions.h"
#include "net/cookies/cookie_constants.h"
#include "url/gurl.h"
#include "url/third_party/mozilla/url_parse.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace net {
namespace {
// Undocumented property of NSHTTPCookie.
NSString* const kNSHTTPCookieHttpOnly = @"HttpOnly";
// Possible value for SameSite policy. WebKit doesn't use the value "none" or
// any other value, it uses the empty value to represent none, and any value
// that is not "strict" or "lax" will be considered as none.
NSString* const kNSHTTPCookieSameSiteNone = @"none";
} // namespace
// Converts NSHTTPCookie to net::CanonicalCookie.
std::unique_ptr<net::CanonicalCookie> CanonicalCookieFromSystemCookie(
NSHTTPCookie* cookie,
const base::Time& ceation_time) {
net::CookieSameSite same_site = net::CookieSameSite::NO_RESTRICTION;
if (@available(iOS 13, *)) {
same_site = net::CookieSameSite::UNSPECIFIED;
if ([cookie.sameSitePolicy isEqual:NSHTTPCookieSameSiteLax])
same_site = net::CookieSameSite::LAX_MODE;
if ([cookie.sameSitePolicy isEqual:NSHTTPCookieSameSiteStrict])
same_site = net::CookieSameSite::STRICT_MODE;
if ([[cookie.sameSitePolicy lowercaseString]
isEqual:kNSHTTPCookieSameSiteNone])
same_site = net::CookieSameSite::NO_RESTRICTION;
}
return net::CanonicalCookie::FromStorage(
base::SysNSStringToUTF8([cookie name]),
base::SysNSStringToUTF8([cookie value]),
base::SysNSStringToUTF8([cookie domain]),
base::SysNSStringToUTF8([cookie path]), ceation_time,
base::Time::FromDoubleT([[cookie expiresDate] timeIntervalSince1970]),
base::Time(), [cookie isSecure], [cookie isHTTPOnly], same_site,
// When iOS begins to support 'Priority' and 'SameParty' attributes, pass
// them through here.
net::COOKIE_PRIORITY_DEFAULT, false /* SameParty */,
absl::nullopt /* partition_key */, net::CookieSourceScheme::kUnset,
url::PORT_UNSPECIFIED);
}
void ReportGetCookiesForURLResult(SystemCookieStoreType store_type,
bool has_cookies) {
GetCookiesForURLCallResult call_result =
GetCookiesForURLCallResult::kCookiesFoundOnWKHTTPSystemCookieStore;
switch (store_type) {
case SystemCookieStoreType::kWKHTTPSystemCookieStore:
call_result =
has_cookies
? GetCookiesForURLCallResult::
kCookiesFoundOnWKHTTPSystemCookieStore
: GetCookiesForURLCallResult::kNoCookiesOnWKHTTPSystemCookieStore;
break;
case SystemCookieStoreType::kNSHTTPSystemCookieStore:
call_result =
has_cookies
? GetCookiesForURLCallResult::
kCookiesFoundOnNSHTTPSystemCookieStore
: GetCookiesForURLCallResult::kNoCookiesOnNSHTTPSystemCookieStore;
break;
case SystemCookieStoreType::kCookieMonster:
call_result =
has_cookies ? GetCookiesForURLCallResult::kCookiesFoundOnCookieMonster
: GetCookiesForURLCallResult::kNoCookiesOnCookieMonster;
break;
}
UMA_HISTOGRAM_ENUMERATION("IOS.Cookies.GetCookiesForURLCallResult",
call_result);
}
void ReportGetCookiesForURLCall(SystemCookieStoreType store_type) {
UMA_HISTOGRAM_ENUMERATION("IOS.Cookies.GetCookiesForURLCallStoreType",
store_type);
}
// Converts net::CanonicalCookie to NSHTTPCookie.
NSHTTPCookie* SystemCookieFromCanonicalCookie(
const net::CanonicalCookie& cookie) {
NSString* cookie_domain = base::SysUTF8ToNSString(cookie.Domain());
NSString* cookie_name = base::SysUTF8ToNSString(cookie.Name());
NSString* cookie_path = base::SysUTF8ToNSString(cookie.Path());
NSString* cookie_value = base::SysUTF8ToNSString(cookie.Value());
if (!cookie_domain || !cookie_name || !cookie_path || !cookie_value) {
DLOG(ERROR) << "Cannot create system cookie: " << cookie.DebugString();
return nil;
}
NSMutableDictionary* properties =
[NSMutableDictionary dictionaryWithDictionary:@{
NSHTTPCookieDomain : cookie_domain,
NSHTTPCookieName : cookie_name,
NSHTTPCookiePath : cookie_path,
NSHTTPCookieValue : cookie_value,
}];
if (cookie.IsPersistent()) {
NSDate* expiry =
[NSDate dateWithTimeIntervalSince1970:cookie.ExpiryDate().ToDoubleT()];
[properties setObject:expiry forKey:NSHTTPCookieExpires];
}
if (@available(iOS 13, *)) {
// In iOS 13 sameSite property in NSHTTPCookie is used to specify the
// samesite policy.
NSString* same_site = @"";
switch (cookie.SameSite()) {
case net::CookieSameSite::LAX_MODE:
same_site = NSHTTPCookieSameSiteLax;
break;
case net::CookieSameSite::STRICT_MODE:
same_site = NSHTTPCookieSameSiteStrict;
break;
case net::CookieSameSite::NO_RESTRICTION:
same_site = kNSHTTPCookieSameSiteNone;
break;
case net::CookieSameSite::UNSPECIFIED:
// All other values of same site policy will be treated as no value .
break;
}
properties[NSHTTPCookieSameSitePolicy] = same_site;
}
if (cookie.IsSecure())
[properties setObject:@"Y" forKey:NSHTTPCookieSecure];
if (cookie.IsHttpOnly())
[properties setObject:@YES forKey:kNSHTTPCookieHttpOnly];
NSHTTPCookie* system_cookie = [NSHTTPCookie cookieWithProperties:properties];
DCHECK(system_cookie);
return system_cookie;
}
NSArray<NSHTTPCookie*>* SystemCookiesFromCanonicalCookieList(
const net::CookieList& cookie_list) {
NSMutableArray<NSHTTPCookie*>* cookies = [[NSMutableArray alloc] init];
for (const net::CanonicalCookie& cookie : cookie_list) {
[cookies addObject:net::SystemCookieFromCanonicalCookie(cookie)];
}
return [cookies copy];
}
} // namespace net