[Curl] Use SQLite database in cookie jar implementation for Curl port
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Feb 2018 00:08:41 +0000 (00:08 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Feb 2018 00:08:41 +0000 (00:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174942

Patch by Christopher Reid <chris.reid@sony.com> on 2018-02-01
Reviewed by Alex Christensen.

No new tests, Set-Cookie is already tested in Layout tests.

Adding an initial SQLite CookieJar implementation to the curl network layer.
WebCore will now parse and handle both HTTP and DOM cookies instead of using libcurl.
This currently supports cookie storage and retrieval.
Cookie deletion is not yet implemented.

* platform/Curl.cmake:
* platform/network/NetworkStorageSession.h: Added cookieDB storage in curl.
* platform/network/curl/CookieJarCurl.cpp: Removed the old curl cookie handling.
* platform/network/curl/CookieJarCurl.h:
* platform/network/curl/CookieJarCurlDatabase.cpp: Added.
* platform/network/curl/CookieJarCurlDatabase.h: Added.
* platform/network/curl/CookieJarDB.cpp: Added.
* platform/network/curl/CookieJarDB.h: Added.
* platform/network/curl/CookieUtil.cpp: Added.
* platform/network/curl/CookieUtil.h: Added.
* platform/network/curl/CurlContext.cpp: Removed the old curl cookie handling.
* platform/network/curl/CurlContext.h:
* platform/network/curl/CurlRequest.cpp: Added handlers for HTTP response cookies.
* platform/network/curl/NetworkStorageSessionCurl.cpp:
* platform/network/curl/ResourceHandleCurlDelegate.cpp:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@227987 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/Curl.cmake
Source/WebCore/platform/network/NetworkStorageSession.h
Source/WebCore/platform/network/curl/CookieJarCurl.cpp
Source/WebCore/platform/network/curl/CookieJarCurl.h
Source/WebCore/platform/network/curl/CookieJarCurlDatabase.cpp [new file with mode: 0644]
Source/WebCore/platform/network/curl/CookieJarCurlDatabase.h [new file with mode: 0644]
Source/WebCore/platform/network/curl/CookieJarDB.cpp [new file with mode: 0644]
Source/WebCore/platform/network/curl/CookieJarDB.h [new file with mode: 0644]
Source/WebCore/platform/network/curl/CookieUtil.cpp [new file with mode: 0644]
Source/WebCore/platform/network/curl/CookieUtil.h [new file with mode: 0644]
Source/WebCore/platform/network/curl/CurlContext.cpp
Source/WebCore/platform/network/curl/CurlContext.h
Source/WebCore/platform/network/curl/CurlRequest.cpp
Source/WebCore/platform/network/curl/NetworkStorageSessionCurl.cpp
Source/WebCore/platform/network/curl/ResourceHandleCurlDelegate.cpp

index f6b98c4..6eba4c6 100644 (file)
@@ -1,3 +1,33 @@
+2018-02-01  Christopher Reid  <chris.reid@sony.com>
+
+        [Curl] Use SQLite database in cookie jar implementation for Curl port
+        https://bugs.webkit.org/show_bug.cgi?id=174942
+
+        Reviewed by Alex Christensen.
+
+        No new tests, Set-Cookie is already tested in Layout tests.
+
+        Adding an initial SQLite CookieJar implementation to the curl network layer.
+        WebCore will now parse and handle both HTTP and DOM cookies instead of using libcurl.
+        This currently supports cookie storage and retrieval.
+        Cookie deletion is not yet implemented.
+
+        * platform/Curl.cmake:
+        * platform/network/NetworkStorageSession.h: Added cookieDB storage in curl.
+        * platform/network/curl/CookieJarCurl.cpp: Removed the old curl cookie handling.
+        * platform/network/curl/CookieJarCurl.h:
+        * platform/network/curl/CookieJarCurlDatabase.cpp: Added.
+        * platform/network/curl/CookieJarCurlDatabase.h: Added.
+        * platform/network/curl/CookieJarDB.cpp: Added.
+        * platform/network/curl/CookieJarDB.h: Added.
+        * platform/network/curl/CookieUtil.cpp: Added.
+        * platform/network/curl/CookieUtil.h: Added.
+        * platform/network/curl/CurlContext.cpp: Removed the old curl cookie handling.
+        * platform/network/curl/CurlContext.h:
+        * platform/network/curl/CurlRequest.cpp: Added handlers for HTTP response cookies.
+        * platform/network/curl/NetworkStorageSessionCurl.cpp:
+        * platform/network/curl/ResourceHandleCurlDelegate.cpp:
+
 2018-02-01  Chris Dumez  <cdumez@apple.com>
 
         Add missing RETURN_IF_EXCEPTION() after object->get() calls in convertDictionary<>()
index cdaef5b..73c014f 100644 (file)
@@ -5,7 +5,10 @@ list(APPEND WebCore_INCLUDE_DIRECTORIES
 list(APPEND WebCore_SOURCES
     platform/network/curl/AuthenticationChallengeCurl.cpp
     platform/network/curl/CookieJarCurl.cpp
+    platform/network/curl/CookieJarCurlDatabase.cpp
+    platform/network/curl/CookieJarDB.cpp
     platform/network/curl/CookieStorageCurl.cpp
+    platform/network/curl/CookieUtil.cpp
     platform/network/curl/CredentialStorageCurl.cpp
     platform/network/curl/CurlCacheEntry.cpp
     platform/network/curl/CurlCacheManager.cpp
index 611dbd9..63c9cf0 100644 (file)
 typedef struct _SoupCookieJar SoupCookieJar;
 #endif
 
+#if USE(CURL)
+#include "CookieJarCurl.h"
+#include "CookieJarDB.h"
+#include <wtf/UniqueRef.h>
+#endif
+
 #ifdef __OBJC__
 #include <objc/objc.h>
 #endif
@@ -116,6 +122,14 @@ public:
     void setCookieObserverHandler(Function<void ()>&&);
     void getCredentialFromPersistentStorage(const ProtectionSpace&, Function<void (Credential&&)> completionHandler);
     void saveCredentialToPersistentStorage(const ProtectionSpace&, const Credential&);
+#elif USE(CURL)
+    NetworkStorageSession(PAL::SessionID, NetworkingContext*);
+    ~NetworkStorageSession();
+
+    const CookieJarCurl& cookieStorage() const { return m_cookieStorage; };
+    CookieJarDB& cookieDatabase() const { return m_cookieDatabase; };
+
+    NetworkingContext* context() const;
 #else
     NetworkStorageSession(PAL::SessionID, NetworkingContext*);
     ~NetworkStorageSession();
@@ -147,6 +161,11 @@ private:
     Function<void (Credential&&)> m_persisentStorageCompletionHandler;
     GRefPtr<GCancellable> m_persisentStorageCancellable;
 #endif
+#elif USE(CURL)
+    UniqueRef<CookieJarCurl> m_cookieStorage;
+    mutable CookieJarDB m_cookieDatabase;
+
+    RefPtr<NetworkingContext> m_context;
 #else
     RefPtr<NetworkingContext> m_context;
 #endif
index e3b728b..329f3a5 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
 
 #if USE(CURL)
 #include "Cookie.h"
-#include "CurlContext.h"
-#include "NotImplemented.h"
+#include "NetworkStorageSession.h"
 #include "URL.h"
 
-#include <wtf/DateMath.h>
-#include <wtf/HashMap.h>
-#include <wtf/text/StringBuilder.h>
-#include <wtf/text/StringHash.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
-static void readCurlCookieToken(const char*& cookie, String& token)
-{
-    // Read the next token from a cookie with the Netscape cookie format.
-    // Curl separates each token in line with tab character.
-    const char* cookieStart = cookie;
-    while (cookie && cookie[0] && cookie[0] != '\t')
-        cookie++;
-    token = String(cookieStart, cookie - cookieStart);
-    if (cookie[0] == '\t')
-        cookie++;
-}
-
-static bool domainMatch(const String& cookieDomain, const String& host)
-{
-    size_t index = host.find(cookieDomain);
-
-    bool tailMatch = (index != WTF::notFound && index + cookieDomain.length() == host.length());
-
-    // Check if host equals cookie domain.
-    if (tailMatch && !index)
-        return true;
-
-    // Check if host is a subdomain of the domain in the cookie.
-    // Curl uses a '.' in front of domains to indicate it's valid on subdomains.
-    if (tailMatch && index > 0 && host[index] == '.')
-        return true;
-
-    // Check the special case where host equals the cookie domain, except for a leading '.' in the cookie domain.
-    // E.g. cookie domain is .apple.com and host is apple.com. 
-    if (cookieDomain[0] == '.' && cookieDomain.find(host) == 1)
-        return true;
-
-    return false;
-}
-
-static void addMatchingCurlCookie(const char* cookie, const String& domain, const String& path, StringBuilder& cookies, bool httponly)
-{
-    // Check if the cookie matches domain and path, and is not expired.
-    // If so, add it to the list of cookies.
-    //
-    // Description of the Netscape cookie file format which Curl uses:
-    //
-    // .netscape.com     TRUE   /  FALSE  946684799   NETSCAPE_ID  100103
-    //
-    // Each line represents a single piece of stored information. A tab is inserted between each of the fields.
-    //
-    // From left-to-right, here is what each field represents:
-    //
-    // domain - The domain that created AND that can read the variable.
-    // flag - A TRUE/FALSE value indicating if all machines within a given domain can access the variable. This value is set automatically by the browser, depending on the value you set for domain.
-    // path - The path within the domain that the variable is valid for.
-    // secure - A TRUE/FALSE value indicating if a secure connection with the domain is needed to access the variable.
-    // expiration - The UNIX time that the variable will expire on. UNIX time is defined as the number of seconds since Jan 1, 1970 00:00:00 GMT.
-    // name - The name of the variable.
-    // value - The value of the variable.
-
-    if (!cookie)
-        return;
-
-    String cookieDomain;
-    readCurlCookieToken(cookie, cookieDomain);
-
-    // HttpOnly cookie entries begin with "#HttpOnly_".
-    if (cookieDomain.startsWith("#HttpOnly_")) {
-        if (httponly)
-            cookieDomain.remove(0, 10);
-        else
-            return;
-    }
-
-    if (!domainMatch(cookieDomain, domain))
-        return;
-
-    String strBoolean;
-    readCurlCookieToken(cookie, strBoolean);
-
-    String strPath;
-    readCurlCookieToken(cookie, strPath);
-
-    // Check if path matches
-    int index = path.find(strPath);
-    if (index)
-        return;
-
-    String strSecure;
-    readCurlCookieToken(cookie, strSecure);
-
-    String strExpires;
-    readCurlCookieToken(cookie, strExpires);
-
-    int expires = strExpires.toInt();
-
-    time_t now = 0;
-    time(&now);
-
-    // Check if cookie has expired
-    if (expires && now > expires)
-        return;
-
-    String strName;
-    readCurlCookieToken(cookie, strName);
-
-    String strValue;
-    readCurlCookieToken(cookie, strValue);
-
-    // The cookie matches, add it to the cookie list.
-
-    if (cookies.length() > 0)
-        cookies.append("; ");
-
-    cookies.append(strName);
-    cookies.append("=");
-    cookies.append(strValue);
-
-}
-
-static String getNetscapeCookieFormat(const URL& url, const String& value)
-{
-    // Constructs a cookie string in Netscape Cookie file format.
-
-    if (value.isEmpty())
-        return "";
-
-    String valueStr;
-    if (value.is8Bit())
-        valueStr = value;
-    else
-        valueStr = String::make8BitFrom16BitSource(value.characters16(), value.length());
-
-    Vector<String> attributes;
-    valueStr.split(';', false, attributes);
-
-    if (!attributes.size())
-        return "";
-
-    // First attribute should be <cookiename>=<cookievalue>
-    String cookieName, cookieValue;
-    Vector<String>::iterator attribute = attributes.begin();
-    if (attribute->contains('=')) {
-        Vector<String> nameValuePair;
-        attribute->split('=', true, nameValuePair);
-        cookieName = nameValuePair[0];
-        cookieValue = nameValuePair[1];
-    } else {
-        // According to RFC6265 we should ignore the entire
-        // set-cookie string now, but other browsers appear
-        // to treat this as <cookiename>=<empty>
-        cookieName = *attribute;
-    }
-    
-    int expires = 0;
-    String secure = "FALSE";
-    String path = url.baseAsString().substring(url.pathStart());
-    if (path.length() > 1 && path.endsWith('/'))
-        path.remove(path.length() - 1);
-    String domain = url.host();
-
-    // Iterate through remaining attributes
-    for (++attribute; attribute != attributes.end(); ++attribute) {
-        if (attribute->contains('=')) {
-            Vector<String> keyValuePair;
-            attribute->split('=', true, keyValuePair);
-            String key = keyValuePair[0].stripWhiteSpace();
-            String val = keyValuePair[1].stripWhiteSpace();
-            if (equalLettersIgnoringASCIICase(key, "expires")) {
-                CString dateStr(reinterpret_cast<const char*>(val.characters8()), val.length());
-                expires = WTF::parseDateFromNullTerminatedCharacters(dateStr.data()) / WTF::msPerSecond;
-            } else if (equalLettersIgnoringASCIICase(key, "max-age"))
-                expires = time(0) + val.toInt();
-            else if (equalLettersIgnoringASCIICase(key, "domain"))
-                domain = val;
-            else if (equalLettersIgnoringASCIICase(key, "path"))
-                path = val;
-        } else {
-            String key = attribute->stripWhiteSpace();
-            if (equalLettersIgnoringASCIICase(key, "secure"))
-                secure = "TRUE";
-        }
-    }
-    
-    String allowSubdomains = domain.startsWith('.') ? "TRUE" : "FALSE";
-    String expiresStr = String::number(expires);
-
-    int finalStringLength = domain.length() + path.length() + expiresStr.length() + cookieName.length();
-    finalStringLength += cookieValue.length() + secure.length() + allowSubdomains.length();
-    finalStringLength += 6; // Account for \t separators.
-    
-    StringBuilder cookieStr;
-    cookieStr.reserveCapacity(finalStringLength);
-    cookieStr.append(domain);
-    cookieStr.append("\t");
-    cookieStr.append(allowSubdomains);
-    cookieStr.append("\t");
-    cookieStr.append(path);
-    cookieStr.append("\t");
-    cookieStr.append(secure);
-    cookieStr.append("\t");
-    cookieStr.append(expiresStr);
-    cookieStr.append("\t");
-    cookieStr.append(cookieName);
-    cookieStr.append("\t");
-    cookieStr.append(cookieValue);
-
-    return cookieStr.toString();
-}
-
-void CookieJarCurlFileSystem::setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
-{
-    UNUSED_PARAM(frameID);
-    UNUSED_PARAM(pageID);
-    CurlHandle curlHandle;
-
-    curlHandle.enableShareHandle();
-    curlHandle.enableCookieJarIfExists();
-
-    // CURL accepts cookies in either Set-Cookie or Netscape file format.
-    // However with Set-Cookie format, there is no way to specify that we
-    // should not allow cookies to be read from subdomains, which is the
-    // required behavior if the domain field is not explicity specified.
-    String cookie = getNetscapeCookieFormat(url, value);
-
-    if (!cookie.is8Bit())
-        cookie = String::make8BitFrom16BitSource(cookie.characters16(), cookie.length());
-
-    CString strCookie(reinterpret_cast<const char*>(cookie.characters8()), cookie.length());
-
-    curlHandle.setCookieList(strCookie.data());
-}
-
-static String cookiesForSession(const NetworkStorageSession&, const URL&, const URL& url, bool httponly)
-{
-    String cookies;
-
-    CurlHandle curlHandle;
-    curlHandle.enableShareHandle();
-
-    CurlSList cookieList;
-    curlHandle.fetchCookieList(cookieList);
-    const struct curl_slist* list = cookieList.head();
-    if (list) {
-        String domain = url.host();
-        String path = url.path();
-        StringBuilder cookiesBuilder;
-
-        while (list) {
-            const char* cookie = list->data;
-            addMatchingCurlCookie(cookie, domain, path, cookiesBuilder, httponly);
-            list = list->next;
-        }
-
-        cookies = cookiesBuilder.toString();
-    }
-
-    return cookies;
-}
-
-std::pair<String, bool> CookieJarCurlFileSystem::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies)
-{
-    UNUSED_PARAM(frameID);
-    UNUSED_PARAM(pageID);
-    // FIXME: This should filter secure cookies out if the caller requests it.
-    return { cookiesForSession(session, firstParty, url, false), false };
-}
-
-std::pair<String, bool> CookieJarCurlFileSystem::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies)
-{
-    UNUSED_PARAM(frameID);
-    UNUSED_PARAM(pageID);
-    // FIXME: This should filter secure cookies out if the caller requests it.
-    return { cookiesForSession(session, firstParty, url, true), false };
-}
-
-bool CookieJarCurlFileSystem::cookiesEnabled(const NetworkStorageSession&)
-{
-    return true;
-}
-
-bool CookieJarCurlFileSystem::getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
-{
-    UNUSED_PARAM(frameID);
-    UNUSED_PARAM(pageID);
-    // FIXME: Not yet implemented
-    rawCookies.clear();
-    return false; // return true when implemented
-}
-
-void CookieJarCurlFileSystem::deleteCookie(const NetworkStorageSession&, const URL&, const String&)
-{
-    // FIXME: Not yet implemented
-}
-
-void CookieJarCurlFileSystem::getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames)
-{
-    // FIXME: Not yet implemented
-}
-
-void CookieJarCurlFileSystem::deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames)
-{
-    // FIXME: Not yet implemented
-}
-
-void CookieJarCurlFileSystem::deleteAllCookies(const NetworkStorageSession&)
-{
-    // FIXME: Not yet implemented
-}
-
-void CookieJarCurlFileSystem::deleteAllCookiesModifiedSince(const NetworkStorageSession&, WallTime)
-{
-    // FIXME: Not yet implemented
-}
-
-// dispatcher functions
-
 std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return CurlContext::singleton().cookieJar().cookiesForDOM(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return session.cookieStorage().cookiesForDOM(session, firstParty, url, frameID, pageID, includeSecureCookies);
 }
 
 void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
 {
-    CurlContext::singleton().cookieJar().setCookiesFromDOM(session, firstParty, url, frameID, pageID, value);
+    session.cookieStorage().setCookiesFromDOM(session, firstParty, url, frameID, pageID, value);
 }
 
 std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
 {
-    return CurlContext::singleton().cookieJar().cookieRequestHeaderFieldValue(session, firstParty, url, frameID, pageID, includeSecureCookies);
+    return session.cookieStorage().cookieRequestHeaderFieldValue(session, firstParty, url, frameID, pageID, includeSecureCookies);
 }
 
 bool cookiesEnabled(const NetworkStorageSession& session)
 {
-    return CurlContext::singleton().cookieJar().cookiesEnabled(session);
+    return session.cookieStorage().cookiesEnabled(session);
 }
 
 bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
 {
-    return CurlContext::singleton().cookieJar().getRawCookies(session, firstParty, url, frameID, pageID, rawCookies);
+    return session.cookieStorage().getRawCookies(session, firstParty, url, frameID, pageID, rawCookies);
 }
 
 void deleteCookie(const NetworkStorageSession& session, const URL& url, const String& cookie)
 {
-    CurlContext::singleton().cookieJar().deleteCookie(session, url, cookie);
+    session.cookieStorage().deleteCookie(session, url, cookie);
 }
 
 void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames)
 {
-    CurlContext::singleton().cookieJar().getHostnamesWithCookies(session, hostnames);
+    session.cookieStorage().getHostnamesWithCookies(session, hostnames);
 }
 
 void deleteCookiesForHostnames(const NetworkStorageSession& session, const Vector<String>& cookieHostNames)
 {
-    CurlContext::singleton().cookieJar().deleteCookiesForHostnames(session, cookieHostNames);
+    session.cookieStorage().deleteCookiesForHostnames(session, cookieHostNames);
 }
 
 void deleteAllCookies(const NetworkStorageSession& session)
 {
-    CurlContext::singleton().cookieJar().deleteAllCookies(session);
+    session.cookieStorage().deleteAllCookies(session);
 }
 
 void deleteAllCookiesModifiedSince(const NetworkStorageSession& session, WallTime since)
 {
-    CurlContext::singleton().cookieJar().deleteAllCookiesModifiedSince(session, since);
+    session.cookieStorage().deleteAllCookiesModifiedSince(session, since);
 }
 
 }
index a52fd78..b8c5469 100644 (file)
@@ -28,29 +28,17 @@ class URL;
 
 class CookieJarCurl {
 public:
-    virtual std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) = 0;
-    virtual void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) = 0;
-    virtual bool cookiesEnabled(const NetworkStorageSession&) = 0;
-    virtual std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) = 0;
-    virtual bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) = 0;
-    virtual void deleteCookie(const NetworkStorageSession&, const URL&, const String&) = 0;
-    virtual void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames) = 0;
-    virtual void deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames) = 0;
-    virtual void deleteAllCookies(const NetworkStorageSession&) = 0;
-    virtual void deleteAllCookiesModifiedSince(const NetworkStorageSession&, WallTime) = 0;
-};
-
-class CookieJarCurlFileSystem : public CookieJarCurl {
-    std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) override;
-    void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) override;
-    bool cookiesEnabled(const NetworkStorageSession&) override;
-    std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) override;
-    bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) override;
-    void deleteCookie(const NetworkStorageSession&, const URL&, const String&) override;
-    void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames) override;
-    void deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames) override;
-    void deleteAllCookies(const NetworkStorageSession&) override;
-    void deleteAllCookiesModifiedSince(const NetworkStorageSession&, WallTime) override;
+    virtual std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const = 0;
+    virtual void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) const = 0;
+    virtual void setCookiesFromHTTPResponse(const NetworkStorageSession&, const URL&, const String&) const = 0;
+    virtual bool cookiesEnabled(const NetworkStorageSession&) const = 0;
+    virtual std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const = 0;
+    virtual bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) const = 0;
+    virtual void deleteCookie(const NetworkStorageSession&, const URL&, const String&) const = 0;
+    virtual void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames) const = 0;
+    virtual void deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames) const = 0;
+    virtual void deleteAllCookies(const NetworkStorageSession&) const = 0;
+    virtual void deleteAllCookiesModifiedSince(const NetworkStorageSession&, WallTime) const = 0;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/network/curl/CookieJarCurlDatabase.cpp b/Source/WebCore/platform/network/curl/CookieJarCurlDatabase.cpp
new file mode 100644 (file)
index 0000000..944fa89
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "config.h"
+#include "CookieJarCurlDatabase.h"
+
+#if USE(CURL)
+#include "Cookie.h"
+#include "CookieJarDB.h"
+#include "NetworkStorageSession.h"
+#include "NotImplemented.h"
+#include "URL.h"
+
+#include <wtf/Optional.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+static String cookiesForSession(const NetworkStorageSession& session, const URL&, const URL& url, bool httponly)
+{
+    StringBuilder cookies;
+
+    CookieJarDB& cookieJarDB = session.cookieDatabase();
+    auto isHttpOnly = (httponly ? std::nullopt : std::optional<bool> {false});
+    auto secure = url.protocolIs("https") ? std::nullopt : std::optional<bool> {false};
+
+    Vector<Cookie> results;
+    if (cookieJarDB.searchCookies(url.string(), isHttpOnly, secure, std::nullopt, results)) {
+        for (auto result : results) {
+            if (!cookies.isEmpty())
+                cookies.append("; ");
+            cookies.append(result.name);
+            cookies.append("=");
+            cookies.append(result.value);
+        }
+    }
+    return cookies.toString();
+}
+
+void CookieJarCurlDatabase::setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value) const
+{
+    UNUSED_PARAM(frameID);
+    UNUSED_PARAM(pageID);
+
+    CookieJarDB& cookieJarDB = session.cookieDatabase();
+    cookieJarDB.setCookie(url.string(), value, true);
+}
+
+void CookieJarCurlDatabase::setCookiesFromHTTPResponse(const NetworkStorageSession& session, const URL& url, const String& value) const
+{
+    CookieJarDB& cookieJarDB = session.cookieDatabase();
+    cookieJarDB.setCookie(url.string(), value, false);
+}
+
+std::pair<String, bool> CookieJarCurlDatabase::cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const
+{
+    UNUSED_PARAM(frameID);
+    UNUSED_PARAM(pageID);
+
+    // FIXME: This should filter secure cookies out if the caller requests it.
+    return { cookiesForSession(session, firstParty, url, false), false };
+}
+
+std::pair<String, bool> CookieJarCurlDatabase::cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const
+{
+    UNUSED_PARAM(frameID);
+    UNUSED_PARAM(pageID);
+
+    // FIXME: This should filter secure cookies out if the caller requests it.
+    return { cookiesForSession(session, firstParty, url, true), false };
+}
+
+bool CookieJarCurlDatabase::cookiesEnabled(const NetworkStorageSession& session) const
+{
+    return true;
+}
+
+bool CookieJarCurlDatabase::getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies) const
+{
+    UNUSED_PARAM(frameID);
+    UNUSED_PARAM(pageID);
+
+    CookieJarDB& cookieJarDB = session.cookieDatabase();
+    return cookieJarDB.searchCookies(firstParty.string(), std::nullopt, std::nullopt, std::nullopt, rawCookies);
+}
+
+void CookieJarCurlDatabase::deleteCookie(const NetworkStorageSession&, const URL& url, const String& cookieName) const
+{
+    // FIXME: Not yet implemented
+}
+
+void CookieJarCurlDatabase::getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames) const
+{
+    // FIXME: Not yet implemented
+}
+
+void CookieJarCurlDatabase::deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames) const
+{
+    // FIXME: Not yet implemented
+}
+
+void CookieJarCurlDatabase::deleteAllCookies(const NetworkStorageSession&) const
+{
+    // FIXME: Not yet implemented
+}
+
+void CookieJarCurlDatabase::deleteAllCookiesModifiedSince(const NetworkStorageSession&, WallTime) const
+{
+    // FIXME: Not yet implemented
+}
+
+} // namespace WebCore
+
+#endif // USE(CURL)
diff --git a/Source/WebCore/platform/network/curl/CookieJarCurlDatabase.h b/Source/WebCore/platform/network/curl/CookieJarCurlDatabase.h
new file mode 100644 (file)
index 0000000..d1d6651
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CookieJarCurl.h"
+
+namespace WebCore {
+
+class CookieJarCurlDatabase : public CookieJarCurl {
+    std::pair<String, bool> cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const override;
+    void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String&) const override;
+    void setCookiesFromHTTPResponse(const NetworkStorageSession&, const URL&, const String&) const override;
+    bool cookiesEnabled(const NetworkStorageSession&) const override;
+    std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies) const override;
+    bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>&) const override;
+    void deleteCookie(const NetworkStorageSession&, const URL&, const String&) const override;
+    void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames) const override;
+    void deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames) const override;
+    void deleteAllCookies(const NetworkStorageSession&) const override;
+    void deleteAllCookiesModifiedSince(const NetworkStorageSession&, WallTime) const override;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/curl/CookieJarDB.cpp b/Source/WebCore/platform/network/curl/CookieJarDB.cpp
new file mode 100644 (file)
index 0000000..16623c1
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CookieJarDB.h"
+
+#include "CookieUtil.h"
+#include "SQLiteFileSystem.h"
+#include "URL.h"
+
+namespace WebCore {
+
+#define CORRUPT_MARKER_SUFFIX "-corrupted"
+
+// At least 50 cookies per domain (RFC6265 6.1. Limits)
+#define MAX_COOKIE_PER_DOMAIN 80
+
+#define CREATE_COOKIE_TABLE_SQL \
+    "CREATE TABLE IF NOT EXISTS Cookie ("\
+    "  name TEXT NOT NULL,"\
+    "  value TEXT,"\
+    "  domain TEXT NOT NULL,"\
+    "  path TEXT NOT NULL,"\
+    "  expires INTEGER NOT NULL,"\
+    "  size INTEGER NOT NULL,"\
+    "  session INTEGER NOT NULL,"\
+    "  httponly INTEGER NOT NULL DEFAULT 0,"\
+    "  secure INTEGER NOT NULL DEFAULT 0,"\
+    "  lastupdated INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, "\
+    "  UNIQUE(name, domain, path));"
+#define CREATE_DOMAIN_INDEX_SQL \
+    "CREATE INDEX IF NOT EXISTS domain_index ON Cookie(domain);"
+#define CREATE_PATH_INDEX_SQL \
+    "CREATE INDEX IF NOT EXISTS path_index ON Cookie(path);"
+#define CHECK_EXISTS_HTTPONLY_COOKIE_SQL \
+    "SELECT name FROM Cookie WHERE (name = ?) AND (domain = ?) AND (path = ?) AND (httponly = 1);"
+#define SET_COOKIE_SQL \
+    "INSERT OR REPLACE INTO Cookie (name, value, domain, path, expires, size, session, httponly, secure) "\
+    "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"
+#define DELETE_COOKIE_BY_NAME_DOMAIN_PATH_SQL \
+    "DELETE FROM Cookie WHERE name = ? AND domain = ? AND path = ?;"
+#define DELETE_COOKIE_BY_NAME_DOMAIN_SQL \
+    "DELETE FROM Cookie WHERE name = ? AND domain = ?;"
+#define DELETE_ALL_SESSION_COOKIE_SQL \
+    "DELETE FROM Cookie WHERE session = 1;"
+#define DELETE_ALL_COOKIE_SQL \
+    "DELETE FROM Cookie;"
+
+void CookieJarDB::setEnabled(bool enable)
+{
+    CookieJarDB::m_cookieEnable = enable;
+}
+
+CookieJarDB::CookieJarDB(const String& databasePath)
+    : m_databasePath(databasePath)
+{
+    checkDatabaseCorruptionAndRemoveIfNeeded();
+
+    if (!openDatabase())
+        return;
+}
+
+CookieJarDB::~CookieJarDB()
+{
+    closeDatabase();
+}
+
+bool CookieJarDB::openDatabase()
+{
+    if (m_database.isOpen())
+        return true;
+
+    bool existsDatabaseFile = false;
+    if (!isOnMemory())
+        existsDatabaseFile = SQLiteFileSystem::ensureDatabaseFileExists(m_databasePath, false);
+
+    if (existsDatabaseFile) {
+        if (m_database.open(m_databasePath, false)) {
+            if (checkDatabaseValidity())
+                executeSimpleSql(DELETE_ALL_SESSION_COOKIE_SQL);
+            else {
+                // delete database and try to re-create again
+                m_database.close();
+                deleteAllDatabaseFiles();
+                existsDatabaseFile = false;
+            }
+        } else {
+            deleteAllDatabaseFiles();
+            existsDatabaseFile = false;
+        }
+    }
+
+    if (!existsDatabaseFile) {
+        if (m_database.open(m_databasePath, false)) {
+            bool databaseValidity = true;
+            databaseValidity &= (executeSimpleSql(CREATE_COOKIE_TABLE_SQL) == SQLITE_DONE);
+            databaseValidity &= (executeSimpleSql(CREATE_DOMAIN_INDEX_SQL) == SQLITE_DONE);
+            databaseValidity &= (executeSimpleSql(CREATE_PATH_INDEX_SQL) == SQLITE_DONE);
+            if (!databaseValidity) {
+                // give up create database at this time (all cookies on request/response are ignored)
+                m_database.close();
+                deleteAllDatabaseFiles();
+            }
+        }
+    }
+
+    if (!m_database.isOpen())
+        return false;
+
+    executeSimpleSql("PRAGMA temp_store = MEMORY;");
+    executeSimpleSql("PRAGMA synchronous = NORMAL;");
+    executeSimpleSql("PRAGMA journal_mode = WAL;");
+
+    // create prepared statements
+    createPrepareStatement(SET_COOKIE_SQL);
+    createPrepareStatement(CHECK_EXISTS_HTTPONLY_COOKIE_SQL);
+    createPrepareStatement(DELETE_COOKIE_BY_NAME_DOMAIN_PATH_SQL);
+    createPrepareStatement(DELETE_COOKIE_BY_NAME_DOMAIN_SQL);
+
+    return true;
+}
+
+void CookieJarDB::closeDatabase()
+{
+    if (m_database.isOpen()) {
+        for (const auto& statement : m_statements)
+            statement.value.get()->finalize();
+        m_statements.clear();
+        m_database.close();
+    }
+}
+
+String CookieJarDB::getCorruptionMarkerPath() const
+{
+    ASSERT(!isOnMemory());
+
+    return m_databasePath + CORRUPT_MARKER_SUFFIX;
+}
+
+void CookieJarDB::flagDatabaseCorruption()
+{
+    if (isOnMemory())
+        return;
+
+    FILE* f = fopen(getCorruptionMarkerPath().utf8().data(), "wb");
+    fclose(f);
+}
+
+bool CookieJarDB::checkDatabaseCorruptionAndRemoveIfNeeded()
+{
+    if (isOnMemory())
+        return false;
+
+    struct stat st = { 0 };
+    int ret = stat(getCorruptionMarkerPath().utf8().data(), &st);
+    if (!ret)
+        return false;
+
+    deleteAllDatabaseFiles();
+    return true;
+}
+
+bool CookieJarDB::checkSQLiteReturnCode(int actual, int expected)
+{
+    if (!m_detectedDatabaseCorruption) {
+        switch (actual) {
+        case SQLITE_CORRUPT:
+        case SQLITE_SCHEMA:
+        case SQLITE_FORMAT:
+        case SQLITE_NOTADB:
+            flagDatabaseCorruption();
+            m_detectedDatabaseCorruption = true;
+        }
+    }
+
+    return (actual == expected);
+}
+
+bool CookieJarDB::checkDatabaseValidity()
+{
+    ASSERT(m_database.isOpen());
+
+    return m_database.tableExists("Cookie");
+}
+
+void CookieJarDB::deleteAllDatabaseFiles()
+{
+    closeDatabase();
+    if (isOnMemory())
+        return;
+
+    int ret = 0;
+    String removePath;
+
+    // remove marker file (cookie.jar.db-corrupted)
+    ret = remove(removePath.utf8().data());
+
+    // remove shm file (cookie.jar.db-shm)
+    removePath = String(m_databasePath + "-shm");
+    ret = remove(removePath.utf8().data());
+
+    // remove wal file (cookie.jar.db-wal)
+    removePath = String(m_databasePath + "-wal");
+    ret = remove(removePath.utf8().data());
+}
+
+bool CookieJarDB::isEnabled()
+{
+    if (m_databasePath.isEmpty())
+        return false;
+
+    return m_cookieEnable;
+}
+
+bool CookieJarDB::searchCookies(const String& requestUrl, const std::optional<bool>& httpOnly, const std::optional<bool>& secure, const std::optional<bool>& session, Vector<Cookie>& results)
+{
+    if (!isEnabled() || !m_database.isOpen())
+        return false;
+
+    URL requestUrlObj(ParsedURLString, requestUrl);
+    String requestHost(requestUrlObj.host().convertToASCIILowercase());
+    String requestPath(requestUrlObj.path().convertToASCIILowercase());
+
+    if (requestHost.isEmpty())
+        return false;
+
+    if (requestPath.isEmpty())
+        requestPath = "/";
+
+    const String sql =
+        "SELECT name, value, domain, path, expires, httponly, secure, session FROM Cookie WHERE "\
+        "(NOT ((session = 0) AND (datetime(expires, 'unixepoch') < datetime('now')))) "\
+        "AND (httponly = COALESCE(NULLIF(?, -1), httponly)) "\
+        "AND (secure = COALESCE(NULLIF(?, -1), secure)) "\
+        "AND (session = COALESCE(NULLIF(?, -1), session)) "\
+        "AND ((domain = ?) OR (domain GLOB ?))";
+
+    std::unique_ptr<SQLiteStatement> pstmt = std::make_unique<SQLiteStatement>(m_database, sql);
+    pstmt->prepare();
+    pstmt->bindInt(1, httpOnly ? *httpOnly : -1);
+    pstmt->bindInt(2, secure ? *secure : -1);
+    pstmt->bindInt(3, session ? *session : -1);
+    pstmt->bindText(4, requestHost);
+
+    size_t topLevelSeparator = requestHost.reverseFind('.');
+    if (CookieUtil::isIPAddress(requestHost) || topLevelSeparator == notFound)
+        pstmt->bindNull(5);
+    else {
+        /* FIXME: currently we currently do not have a public suffic list in wincairo
+           so right now we glob using just the second level domain e.g. *.DOMAIN.com
+           This will return too many cookies under multilevel tlds such as *.co.uk
+           but we filter those out later
+        */
+
+        size_t secondLevelSeparator = requestHost.reverseFind('.', topLevelSeparator-1);
+        String localDomain = secondLevelSeparator == notFound ? requestHost : requestHost.substring(secondLevelSeparator+1);
+
+        ASSERT(!localDomain.isEmpty());
+        pstmt->bindText(5, String("*.") + localDomain);
+    }
+
+    if (!pstmt)
+        return false;
+
+    results.clear();
+
+    while (pstmt->step() == SQLITE_ROW) {
+
+        if (results.size() > MAX_COOKIE_PER_DOMAIN)
+            break;
+
+        String cookieName = pstmt->getColumnText(0);
+        String cookieValue = pstmt->getColumnText(1);
+        String cookieDomain = pstmt->getColumnText(2).convertToASCIILowercase();
+        String cookiePath = pstmt->getColumnText(3).convertToASCIILowercase();
+        double cookieExpires = (double)pstmt->getColumnInt64(4) * 1000;
+        bool cookieHttpOnly = (pstmt->getColumnInt(5) == 1);
+        bool cookieSecure = (pstmt->getColumnInt(6) == 1);
+        bool cookieSession = (pstmt->getColumnInt(7) == 1);
+
+        if (!CookieUtil::domainMatch(cookieDomain, requestHost))
+            continue;
+
+        // https://tools.ietf.org/html/rfc6265#section-5.1.4 "Paths and Path-Match"
+        bool isPathMatched = cookiePath == requestPath
+            || (requestPath.startsWith(cookiePath) && cookiePath.endsWith('/'))
+            || (requestPath.startsWith(cookiePath) && (requestPath.characterAt(cookiePath.length()) == '/'));
+
+        if (!isPathMatched)
+            continue;
+
+        Cookie result(cookieName,
+            cookieValue,
+            cookieDomain,
+            cookiePath,
+            0,
+            cookieExpires,
+            cookieHttpOnly,
+            cookieSecure,
+            cookieSession,
+            String(),
+            URL(),
+            Vector<uint16_t>());
+
+        results.append(result);
+    }
+    pstmt->finalize();
+
+    return true;
+}
+
+bool CookieJarDB::hasHttpOnlyCookie(const String& name, const String& domain, const String& path)
+{
+    SQLiteStatement* statement = getPrepareStatement(CHECK_EXISTS_HTTPONLY_COOKIE_SQL);
+    ASSERT(statement);
+
+    statement->bindText(1, name);
+    statement->bindText(2, domain);
+    statement->bindText(3, path);
+
+    return statement->step() == SQLITE_ROW;
+}
+
+
+int CookieJarDB::setCookie(const Cookie& cookie)
+{
+    int ret = 0;
+    if (!cookie.session && (cookie.expires < ::time(0)))
+        ret = deleteCookieInternal(cookie.name, cookie.domain, cookie.path);
+    else {
+        SQLiteStatement* statement = getPrepareStatement(SET_COOKIE_SQL);
+        ASSERT(statement);
+
+        // FIXME: We should have some eviction policy when a domain goes over MAX_COOKIE_PER_DOMAIN
+
+        statement->bindText(1, cookie.name);
+        statement->bindText(2, cookie.value);
+        statement->bindText(3, cookie.domain);
+        statement->bindText(4, cookie.path);
+        statement->bindInt64(5, cookie.session ? 0 : (int64_t)cookie.expires);
+        statement->bindInt(6, cookie.value.length());
+        statement->bindInt(7, cookie.session ? 1 : 0);
+        statement->bindInt(8, cookie.httpOnly ? 1 : 0);
+        statement->bindInt(9, cookie.secure ? 1 : 0);
+
+        ret = statement->step();
+    }
+    ASSERT(checkSQLiteReturnCode(ret, SQLITE_DONE));
+
+    return ret;
+}
+
+int CookieJarDB::setCookie(const String& url, const String& cookie, bool fromJavaScript)
+{
+    if (!isEnabled() || !m_database.isOpen())
+        return -1;
+
+    if (url.isEmpty() || cookie.isEmpty())
+        return -1;
+
+    URL urlObj(ParsedURLString, url);
+    String host(urlObj.host());
+    String path(urlObj.path());
+
+    Cookie cookieObj;
+    if (!CookieUtil::parseCookieHeader(cookie, host, cookieObj))
+        return -1;
+
+    if (cookieObj.domain.isEmpty())
+        cookieObj.domain = String(host);
+
+    if (cookieObj.path.isEmpty())
+        cookieObj.path = String(path);
+
+    // FIXME: Need to check that a domain doesn't a set cookie for a tld when wincairo supports PSL
+
+    if (fromJavaScript && cookieObj.httpOnly)
+        return -1;
+
+    if (fromJavaScript && hasHttpOnlyCookie(cookieObj.name, cookieObj.domain, cookieObj.path))
+        return -1;
+
+    return setCookie(cookieObj);
+}
+
+int CookieJarDB::deleteCookie(const String& url, const String& name)
+{
+    if (!isEnabled() || !m_database.isOpen())
+        return -1;
+
+    String urlCopied = String(url);
+    if (urlCopied.startsWith('.'))
+        urlCopied.remove(0, 1);
+
+    URL urlObj(ParsedURLString, urlCopied);
+    if (urlObj.isValid()) {
+        String hostStr(urlObj.host());
+        String pathStr(urlObj.path());
+        int ret = deleteCookieInternal(name, hostStr, pathStr);
+        ASSERT(checkSQLiteReturnCode(ret, SQLITE_DONE));
+
+        return ret;
+    }
+
+    return -1;
+}
+
+int CookieJarDB::deleteCookieInternal(const String& name, const String& domain, const String& path)
+{
+    SQLiteStatement* statement;
+    if (!path.isEmpty()) {
+        statement = getPrepareStatement(DELETE_COOKIE_BY_NAME_DOMAIN_PATH_SQL);
+        ASSERT(statement);
+        statement->bindText(1, name);
+        statement->bindText(2, domain);
+        statement->bindText(3, path);
+    } else {
+        statement = getPrepareStatement(DELETE_COOKIE_BY_NAME_DOMAIN_SQL);
+        ASSERT(statement);
+        statement->bindText(1, name);
+        statement->bindText(2, domain);
+    }
+
+    return statement->step();
+}
+
+int CookieJarDB::deleteCookies(const String& url)
+{
+    // NOT IMPLEMENTED
+    // TODO: this function will be called if application calls WKCookieManagerDeleteCookiesForHostname() in WKCookieManager.h.
+    return 0;
+}
+
+int CookieJarDB::deleteAllCookies()
+{
+    if (!isEnabled() || !m_database.isOpen())
+        return -1;
+
+    return executeSimpleSql(DELETE_ALL_COOKIE_SQL);
+}
+
+void CookieJarDB::createPrepareStatement(const char* sql)
+{
+    auto statement = std::make_unique<SQLiteStatement>(m_database, sql);
+    int ret = statement->prepare();
+    ASSERT(ret == SQLITE_OK);
+    m_statements.add(String(sql), WTFMove(statement));
+}
+
+SQLiteStatement* CookieJarDB::getPrepareStatement(const char* sql)
+{
+    const auto& statement = m_statements.get(String(sql));
+    ASSERT(statement);
+    statement->reset();
+    return statement;
+}
+
+int CookieJarDB::executeSimpleSql(const char* sql, bool ignoreError)
+{
+    SQLiteStatement statement(m_database, sql);
+    int ret = statement.prepareAndStep();
+    statement.finalize();
+
+    ASSERT(checkSQLiteReturnCode(ret, SQLITE_OK)
+        || checkSQLiteReturnCode(ret, SQLITE_DONE)
+        || checkSQLiteReturnCode(ret, SQLITE_ROW)
+        || ignoreError);
+    return ret;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/curl/CookieJarDB.h b/Source/WebCore/platform/network/curl/CookieJarDB.h
new file mode 100644 (file)
index 0000000..5ec309b
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Cookie.h"
+#include "SQLiteDatabase.h"
+#include "SQLiteStatement.h"
+#include <wtf/HashMap.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CookieJarDB {
+    WTF_MAKE_NONCOPYABLE(CookieJarDB);
+
+public:
+    bool isEnabled();
+    void setEnabled(bool);
+
+    bool searchCookies(const String& requestUrl, const std::optional<bool>& httpOnly, const std::optional<bool>& secure, const std::optional<bool>& session, Vector<Cookie>& results);
+    int setCookie(const String& url, const String& cookie, bool fromJavaScript);
+    int setCookie(const Cookie&);
+
+    int deleteCookie(const String& url, const String& name);
+    int deleteCookies(const String& url);
+    int deleteAllCookies();
+
+    CookieJarDB(const String& databasePath);
+    ~CookieJarDB();
+
+private:
+
+    bool m_cookieEnable {true};
+    String m_databasePath;
+
+    bool m_detectedDatabaseCorruption {false};
+
+    bool isOnMemory() const { return (m_databasePath == ":onmemory:"); };
+
+    bool openDatabase();
+    void closeDatabase();
+
+    bool checkSQLiteReturnCode(int actual, int expected);
+    void flagDatabaseCorruption();
+    bool checkDatabaseCorruptionAndRemoveIfNeeded();
+    String getCorruptionMarkerPath() const;
+
+    bool checkDatabaseValidity();
+    void deleteAllDatabaseFiles();
+
+    void createPrepareStatement(const char* sql);
+    SQLiteStatement* getPrepareStatement(const char* sql);
+    int executeSimpleSql(const char* sql, bool ignoreError = false);
+
+    int deleteCookieInternal(const String& name, const String& domain, const String& path);
+    bool hasHttpOnlyCookie(const String& name, const String& domain, const String& path);
+
+    SQLiteDatabase m_database;
+    HashMap<String, std::unique_ptr<SQLiteStatement>> m_statements;
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/network/curl/CookieUtil.cpp b/Source/WebCore/platform/network/curl/CookieUtil.cpp
new file mode 100644 (file)
index 0000000..955e5d1
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CookieUtil.h"
+
+#if USE(CURL)
+
+#include "Cookie.h"
+
+#include <wtf/DateMath.h>
+#include <wtf/Optional.h>
+#include <wtf/text/WTFString.h>
+
+/* This is the maximum line length we accept for a cookie line. RFC 2109
+   section 6.3 says:
+
+   "at least 4096 bytes per cookie (as measured by the size of the characters
+   that comprise the cookie non-terminal in the syntax description of the
+   Set-Cookie header)"
+*/
+
+#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)
+{
+    // Assuming that hosts ending in a digit are IP Addresses
+    return !hostname.isEmpty() && isASCIIDigit(hostname[hostname.length() - 1]);
+}
+
+bool domainMatch(const String& cookieDomain, const String& host)
+{
+    size_t index = host.find(cookieDomain);
+
+    bool tailMatch = (index != WTF::notFound && index + cookieDomain.length() == host.length());
+
+    // Check if host equals cookie domain.
+    if (tailMatch && !index)
+        return true;
+
+    // Check if host is a subdomain of the domain in the cookie.
+    // Curl uses a '.' in front of domains to indicate it's valid on subdomains.
+    if (tailMatch && index > 0 && host[index] == '.')
+        return true;
+
+    // Check the special case where host equals the cookie domain, except for a leading '.' in the cookie domain.
+    // E.g. cookie domain is .apple.com and host is apple.com.
+    if (cookieDomain[0] == '.' && cookieDomain.find(host) == 1)
+        return true;
+
+    return false;
+}
+
+static std::optional<double> parseExpires(const char* expires)
+{
+    double tmp = WTF::parseDateFromNullTerminatedCharacters(expires);
+    if (isnan(tmp))
+        return { };
+
+    return std::optional<double> {tmp / WTF::msPerSecond};
+}
+
+static void parseCookieAttributes(const String& attribute, const String& domain, 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;
+
+        // Enforce a dot character prefix to hostnames which are not ip addresses and not single value hostnames such as localhost
+        if (!isIPAddress(attributeValue) && !attributeValue.startsWith('.') && attributeValue.find('.') != notFound)
+            attributeValue = "." + attributeValue;
+
+        // Make sure the host can set a cookie for the domain
+        // FIXME: firefox and chrome both ignore cookies with no valid domain set
+        // we currently ignore the invalid domains and default to the hostname as the domain
+        if (domainMatch(attributeValue, domain))
+            result.domain = attributeValue;
+
+    } else if (equalIgnoringASCIICase(attributeName, "max-age")) {
+        bool ok;
+        time_t expiryTime = time(0) + attributeValue.toInt64(&ok);
+        if (ok) {
+            result.expires = (double)expiryTime;
+            result.session = false;
+
+            // If there is a max-age attribute as well as an expires attribute
+            // the rightmost max-age attribute takes precedence.
+            hasMaxAge = true;
+        }
+    } else if (equalIgnoringASCIICase(attributeName, "expires") && !hasMaxAge) {
+        if (auto expiryTime = parseExpires(attributeValue.utf8().data())) {
+            result.expires = expiryTime.value();
+            result.session = false;
+        }
+    } else if (equalIgnoringASCIICase(attributeName, "path")) {
+        if (!attributeValue.isEmpty() && attributeValue.startsWith('/'))
+            result.path = attributeValue;
+    }
+}
+
+bool parseCookieHeader(const String& cookieLine, const String& domain, Cookie& result)
+{
+    if (cookieLine.length() >= MAX_COOKIE_LINE)
+        return false;
+
+    // This Algorithm is based on the algorithm defined in RFC 6265 5.2 https://tools.ietf.org/html/rfc6265#section-5.2/
+
+    size_t separatorPosition = cookieLine.find(';');
+
+    String cookiePair = separatorPosition == notFound ? cookieLine : cookieLine.substring(0, separatorPosition);
+
+    String cookieName;
+    String cookieValue;
+    size_t assignmentPosition = cookieLine.find('=');
+
+    // RFC6265 says to ignore cookies pairs with empty names or no assignment character
+    // but browsers seem to treat this type of cookie string as the cookie value
+    if (assignmentPosition == notFound)
+        cookieValue = cookiePair;
+    else {
+        cookieName = cookiePair.substring(0, assignmentPosition);
+        cookieValue = cookiePair.substring(assignmentPosition + 1);
+    }
+
+    result.name = cookieName.stripWhiteSpace();
+    result.value = cookieValue.stripWhiteSpace();
+
+    bool hasMaxAge = false;
+    result.session = true;
+
+    Vector<String> cookieAttributes;
+    cookieLine.split(';', true, cookieAttributes);
+    for (auto attribute : cookieAttributes)
+        parseCookieAttributes(attribute, domain, hasMaxAge, result);
+
+    return true;
+}
+
+} // namespace CookieUtil
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/network/curl/CookieUtil.h b/Source/WebCore/platform/network/curl/CookieUtil.h
new file mode 100644 (file)
index 0000000..267fdc2
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+struct Cookie;
+
+namespace CookieUtil {
+
+bool parseCookieHeader(const String& cookieLine, const String& domain, Cookie& result);
+
+bool isIPAddress(const String& hostname);
+
+bool domainMatch(const String& cookieDomain, const String& host);
+
+} // namespace CookieUtil
+
+} // namespace WebCore
index d54b12c..5a429a9 100644 (file)
 
 namespace WebCore {
 
-static CString cookieJarPath()
-{
-    char* cookieJarPath = getenv("CURL_COOKIE_JAR_PATH");
-    if (cookieJarPath)
-        return cookieJarPath;
-
-#if OS(WINDOWS)
-    char executablePath[MAX_PATH];
-    char appDataDirectory[MAX_PATH];
-    char cookieJarFullPath[MAX_PATH];
-    char cookieJarDirectory[MAX_PATH];
-
-    if (FAILED(::SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, appDataDirectory))
-        || FAILED(::GetModuleFileNameA(0, executablePath, MAX_PATH)))
-        return "cookies.dat";
-
-    ::PathRemoveExtensionA(executablePath);
-    LPSTR executableName = ::PathFindFileNameA(executablePath);
-    sprintf_s(cookieJarDirectory, MAX_PATH, "%s/%s", appDataDirectory, executableName);
-    sprintf_s(cookieJarFullPath, MAX_PATH, "%s/cookies.dat", cookieJarDirectory);
-
-    if (::SHCreateDirectoryExA(0, cookieJarDirectory, 0) != ERROR_SUCCESS
-        && ::GetLastError() != ERROR_FILE_EXISTS
-        && ::GetLastError() != ERROR_ALREADY_EXISTS)
-        return "cookies.dat";
-
-    return cookieJarFullPath;
-#else
-    return "cookies.dat";
-#endif
-}
-
 // CurlContext -------------------------------------------------------------------
 
 CurlContext& CurlContext::singleton()
@@ -83,10 +51,8 @@ CurlContext& CurlContext::singleton()
 }
 
 CurlContext::CurlContext()
-: m_cookieJarFileName { cookieJarPath() }
-, m_cookieJar { std::make_unique<CookieJarCurlFileSystem>() }
 {
-    initCookieSession();
+    initShareHandle();
 
 #ifndef NDEBUG
     m_verbose = getenv("DEBUG_CURL");
@@ -105,13 +71,8 @@ CurlContext::~CurlContext()
 #endif
 }
 
-// Cookie =======================
-
-void CurlContext::initCookieSession()
+void CurlContext::initShareHandle()
 {
-    // Curl saves both persistent cookies, and session cookies to the cookie file.
-    // The session cookies should be deleted before starting a new session.
-
     CURL* curl = curl_easy_init();
 
     if (!curl)
@@ -119,13 +80,6 @@ void CurlContext::initCookieSession()
 
     curl_easy_setopt(curl, CURLOPT_SHARE, m_shareHandle.handle());
 
-    if (!m_cookieJarFileName.isNull()) {
-        curl_easy_setopt(curl, CURLOPT_COOKIEFILE, m_cookieJarFileName.data());
-        curl_easy_setopt(curl, CURLOPT_COOKIEJAR, m_cookieJarFileName.data());
-    }
-
-    curl_easy_setopt(curl, CURLOPT_COOKIESESSION, 1);
-
     curl_easy_cleanup(curl);
 }
 
@@ -483,26 +437,6 @@ void CurlHandle::setSslKeyPassword(const char* password)
     curl_easy_setopt(m_handle, CURLOPT_KEYPASSWD, password);
 }
 
-void CurlHandle::enableCookieJarIfExists()
-{
-    const char* cookieJar = CurlContext::singleton().getCookieJarFileName();
-    if (cookieJar)
-        curl_easy_setopt(m_handle, CURLOPT_COOKIEJAR, cookieJar);
-}
-
-void CurlHandle::setCookieList(const char* cookieList)
-{
-    if (!cookieList)
-        return;
-
-    curl_easy_setopt(m_handle, CURLOPT_COOKIELIST, cookieList);
-}
-
-void CurlHandle::fetchCookieList(CurlSList &cookies) const
-{
-    curl_easy_getinfo(m_handle, CURLINFO_COOKIELIST, static_cast<struct curl_slist**>(cookies));
-}
-
 void CurlHandle::enableProxyIfExists()
 {
     auto& proxy = CurlContext::singleton().proxyInfo();
index 488085e..f52bd55 100644 (file)
@@ -26,7 +26,6 @@
 
 #pragma once
 
-#include "CookieJarCurl.h"
 #include "CurlSSLHandle.h"
 #include "URL.h"
 
@@ -110,11 +109,6 @@ public:
 
     const CurlShareHandle& shareHandle() { return m_shareHandle; }
 
-    // Cookie
-    const char* getCookieJarFileName() const { return m_cookieJarFileName.data(); }
-    void setCookieJarFileName(const char* cookieJarFileName) { m_cookieJarFileName = CString(cookieJarFileName); }
-    CookieJarCurl& cookieJar() { return *m_cookieJar; }
-
     // Proxy
     const ProxyInfo& proxyInfo() const { return m_proxy; }
     void setProxyInfo(const ProxyInfo& info) { m_proxy = info;  }
@@ -133,13 +127,11 @@ public:
 
 private:
     ProxyInfo m_proxy;
-    CString m_cookieJarFileName;
     CurlShareHandle m_shareHandle;
-    std::unique_ptr<CookieJarCurl> m_cookieJar;
     CurlSSLHandle m_sslHandle;
 
     CurlContext();
-    void initCookieSession();
+    void initShareHandle();
 
 #ifndef NDEBUG
     FILE* m_logFile { nullptr };
@@ -256,10 +248,6 @@ public:
     void setSslCertType(const char*);
     void setSslKeyPassword(const char*);
 
-    void enableCookieJarIfExists();
-    void setCookieList(const char*);
-    void fetchCookieList(CurlSList &cookies) const;
-
     void enableProxyIfExists();
 
     void enableTimeout();
index e0a80f4..f3ebd68 100644 (file)
@@ -200,7 +200,6 @@ CURL* CurlRequest::setupTransfer()
     m_curlHandle->setTimeout(timeoutMilliseconds);
 
     m_curlHandle->enableProxyIfExists();
-    m_curlHandle->enableCookieJarIfExists();
 
     m_curlHandle->setSslVerifyPeer(CurlHandle::VerifyPeer::Enable);
     m_curlHandle->setSslVerifyHost(CurlHandle::VerifyHost::StrictNameCheck);
index 176ae9f..b88036a 100644 (file)
 #if USE(CURL)
 
 #include "Cookie.h"
+#include "CookieJarCurlDatabase.h"
+#include "CookieJarDB.h"
+#include "CurlContext.h"
+#include "FileSystem.h"
 #include "NetworkingContext.h"
 #include "ResourceHandle.h"
 
 
 namespace WebCore {
 
+static String defaultCookieJarPath()
+{
+    static const char* defaultFileName = "cookie.jar.db";
+    char* cookieJarPath = getenv("CURL_COOKIE_JAR_PATH");
+    if (cookieJarPath)
+        return cookieJarPath;
+
+    String cookieJarDirectory = FileSystem::localUserSpecificStorageDirectory();
+
+    if (!FileSystem::makeAllDirectories(cookieJarDirectory))
+        return defaultFileName;
+
+    return FileSystem::pathByAppendingComponent(cookieJarDirectory, defaultFileName);
+}
+
 NetworkStorageSession::NetworkStorageSession(PAL::SessionID sessionID, NetworkingContext* context)
     : m_sessionID(sessionID)
     , m_context(context)
+    , m_cookieDatabase(defaultCookieJarPath())
+    , m_cookieStorage(makeUniqueRef<CookieJarCurlDatabase>())
 {
 }
 
index 5149316..00c4465 100644 (file)
 #if USE(CURL)
 
 #include "AuthenticationChallenge.h"
+#include "CookieJarCurl.h"
 #include "CredentialStorage.h"
 #include "CurlCacheManager.h"
 #include "CurlRequest.h"
 #include "HTTPParsers.h"
+#include "NetworkStorageSession.h"
 #include "ResourceHandleInternal.h"
 #include "SharedBuffer.h"
 #include "TextEncoding.h"
@@ -190,6 +192,20 @@ void ResourceHandleCurlDelegate::curlDidSendData(unsigned long long bytesSent, u
     m_handle->client()->didSendData(m_handle, bytesSent, totalBytesToBeSent);
 }
 
+static void handleCookieHeaders(const CurlResponse& response)
+{
+    static const auto setCookieHeader = "set-cookie: ";
+
+    auto& defaultStorageSession = NetworkStorageSession::defaultStorageSession();
+    const CookieJarCurl& cookieJar = defaultStorageSession.cookieStorage();
+    for (auto header : response.headers) {
+        if (header.startsWithIgnoringASCIICase(setCookieHeader)) {
+            String setCookieString = header.right(header.length() - strlen(setCookieHeader));
+            cookieJar.setCookiesFromHTTPResponse(defaultStorageSession, response.url, setCookieString);
+        }
+    }
+}
+
 void ResourceHandleCurlDelegate::curlDidReceiveResponse(const CurlResponse& receivedResponse)
 {
     ASSERT(isMainThread());
@@ -203,6 +219,8 @@ void ResourceHandleCurlDelegate::curlDidReceiveResponse(const CurlResponse& rece
     if (m_curlRequest)
         m_handle->getInternal()->m_response.setDeprecatedNetworkLoadMetrics(m_curlRequest->getNetworkLoadMetrics());
 
+    handleCookieHeaders(receivedResponse);
+
     if (response().shouldRedirect()) {
         willSendRequest();
         return;