#include "config.h"
#include "CookieUtil.h"
#if USE(CURL)
#include "Cookie.h"
#include <wtf/DateMath.h>
#include <wtf/Optional.h>
#include <wtf/WallTime.h>
#include <wtf/text/WTFString.h>
#define MAX_COOKIE_LINE 5000
#define MAX_COOKIE_LINE_TXT "4999"
#define MAX_NAME 1024
#define MAX_NAME_TXT "1023"
namespace WebCore {
namespace CookieUtil {
bool isIPAddress(const String& hostname)
{
return URL::hostIsIPAddress(hostname);
}
bool domainMatch(const String& cookieDomain, const String& host)
{
size_t index = host.find(cookieDomain);
bool tailMatch = (index != WTF::notFound && index + cookieDomain.length() == host.length());
if (tailMatch && !index)
return true;
if (tailMatch && index > 0 && host[index] == '.')
return true;
if (cookieDomain[0] == '.' && cookieDomain.find(host) == 1)
return true;
return false;
}
static Optional<double> parseExpiresMS(const char* expires)
{
double tmp = WTF::parseDateFromNullTerminatedCharacters(expires);
if (isnan(tmp))
return { };
return Optional<double> {tmp};
}
static void parseCookieAttributes(const String& attribute, bool& hasMaxAge, Cookie& result)
{
size_t assignmentPosition = attribute.find('=');
String attributeName;
String attributeValue;
if (assignmentPosition != notFound) {
attributeName = attribute.substring(0, assignmentPosition).stripWhiteSpace();
attributeValue = attribute.substring(assignmentPosition + 1).stripWhiteSpace();
} else
attributeName = attribute.stripWhiteSpace();
if (equalIgnoringASCIICase(attributeName, "httponly"))
result.httpOnly = true;
else if (equalIgnoringASCIICase(attributeName, "secure"))
result.secure = true;
else if (equalIgnoringASCIICase(attributeName, "domain")) {
if (attributeValue.isEmpty())
return;
if (!isIPAddress(attributeValue) && !attributeValue.startsWith('.') && attributeValue.find('.') != notFound)
attributeValue = "." + attributeValue;
result.domain = attributeValue.convertToASCIILowercase();
} else if (equalIgnoringASCIICase(attributeName, "max-age")) {
bool ok;
double maxAgeSeconds = attributeValue.toInt64(&ok);
if (ok) {
result.expires = (WallTime::now().secondsSinceEpoch().value() + maxAgeSeconds) * WTF::msPerSecond;
result.session = false;
hasMaxAge = true;
}
} else if (equalIgnoringASCIICase(attributeName, "expires") && !hasMaxAge) {
if (auto expiryTime = parseExpiresMS(attributeValue.utf8().data())) {
result.expires = expiryTime.value();
result.session = false;
}
} else if (equalIgnoringASCIICase(attributeName, "path")) {
if (!attributeValue.isEmpty() && attributeValue.startsWith('/'))
result.path = attributeValue;
}
}
Optional<Cookie> parseCookieHeader(const String& cookieLine)
{
if (cookieLine.length() >= MAX_COOKIE_LINE)
return WTF::nullopt;
size_t separatorPosition = cookieLine.find(';');
String cookiePair = separatorPosition == notFound ? cookieLine : cookieLine.substring(0, separatorPosition);
String cookieName;
String cookieValue;
size_t assignmentPosition = cookiePair.find('=');
if (assignmentPosition == notFound)
cookieValue = cookiePair;
else {
cookieName = cookiePair.substring(0, assignmentPosition);
cookieValue = cookiePair.substring(assignmentPosition + 1);
}
Cookie cookie;
cookie.name = cookieName.stripWhiteSpace();
cookie.value = cookieValue.stripWhiteSpace();
bool hasMaxAge = false;
cookie.session = true;
for (auto attribute : cookieLine.splitAllowingEmptyEntries(';'))
parseCookieAttributes(attribute, hasMaxAge, cookie);
return cookie;
}
String defaultPathForURL(const URL& url)
{
String path = url.path().toString();
if (path.isEmpty() || !path.startsWith('/'))
return "/";
auto lastSlashPosition = path.reverseFind('/');
if (!lastSlashPosition)
return "/";
return path.substring(0, lastSlashPosition);
}
}
}
#endif