Home | History | Annotate | Download | only in cf
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "CookieJar.h"
     28 
     29 #if USE(CFNETWORK)
     30 
     31 #include "Cookie.h"
     32 #include "CookieStorageCFNet.h"
     33 #include "Document.h"
     34 #include "KURL.h"
     35 #include "PlatformString.h"
     36 #include "ResourceHandle.h"
     37 #include "SoftLinking.h"
     38 #include <CFNetwork/CFHTTPCookiesPriv.h>
     39 #include <CoreFoundation/CoreFoundation.h>
     40 #include <WebKitSystemInterface/WebKitSystemInterface.h>
     41 #include <windows.h>
     42 
     43 namespace WebCore {
     44 
     45 static const CFStringRef s_setCookieKeyCF = CFSTR("Set-Cookie");
     46 static const CFStringRef s_cookieCF = CFSTR("Cookie");
     47 
     48 #ifdef DEBUG_ALL
     49 SOFT_LINK_DEBUG_LIBRARY(CFNetwork)
     50 #else
     51 SOFT_LINK_LIBRARY(CFNetwork)
     52 #endif
     53 
     54 SOFT_LINK_OPTIONAL(CFNetwork, CFHTTPCookieCopyDomain, CFStringRef, __cdecl, (CFHTTPCookieRef))
     55 SOFT_LINK_OPTIONAL(CFNetwork, CFHTTPCookieGetExpirationTime, CFAbsoluteTime, __cdecl, (CFHTTPCookieRef))
     56 SOFT_LINK_OPTIONAL(CFNetwork, CFHTTPCookieCopyName, CFStringRef, __cdecl, (CFHTTPCookieRef))
     57 SOFT_LINK_OPTIONAL(CFNetwork, CFHTTPCookieCopyPath, CFStringRef, __cdecl, (CFHTTPCookieRef))
     58 SOFT_LINK_OPTIONAL(CFNetwork, CFHTTPCookieCopyValue, CFStringRef, __cdecl, (CFHTTPCookieRef))
     59 
     60 static inline RetainPtr<CFStringRef> cookieDomain(CFHTTPCookieRef cookie)
     61 {
     62     if (CFHTTPCookieCopyDomainPtr())
     63         return RetainPtr<CFStringRef>(AdoptCF, CFHTTPCookieCopyDomainPtr()(cookie));
     64     return CFHTTPCookieGetDomain(cookie);
     65 }
     66 
     67 static inline CFAbsoluteTime cookieExpirationTime(CFHTTPCookieRef cookie)
     68 {
     69     if (CFHTTPCookieGetExpirationTimePtr())
     70         return CFHTTPCookieGetExpirationTimePtr()(cookie);
     71     return CFDateGetAbsoluteTime(CFHTTPCookieGetExpiratonDate(cookie));
     72 }
     73 
     74 static inline RetainPtr<CFStringRef> cookieName(CFHTTPCookieRef cookie)
     75 {
     76     if (CFHTTPCookieCopyNamePtr())
     77         return RetainPtr<CFStringRef>(AdoptCF, CFHTTPCookieCopyNamePtr()(cookie));
     78     return CFHTTPCookieGetName(cookie);
     79 }
     80 
     81 static inline RetainPtr<CFStringRef> cookiePath(CFHTTPCookieRef cookie)
     82 {
     83     if (CFHTTPCookieCopyPathPtr())
     84         return RetainPtr<CFStringRef>(AdoptCF, CFHTTPCookieCopyPathPtr()(cookie));
     85     return CFHTTPCookieGetPath(cookie);
     86 }
     87 
     88 static inline RetainPtr<CFStringRef> cookieValue(CFHTTPCookieRef cookie)
     89 {
     90     if (CFHTTPCookieCopyValuePtr())
     91         return RetainPtr<CFStringRef>(AdoptCF, CFHTTPCookieCopyValuePtr()(cookie));
     92     return CFHTTPCookieGetValue(cookie);
     93 }
     94 
     95 static RetainPtr<CFArrayRef> filterCookies(CFArrayRef unfilteredCookies)
     96 {
     97     CFIndex count = CFArrayGetCount(unfilteredCookies);
     98     RetainPtr<CFMutableArrayRef> filteredCookies(AdoptCF, CFArrayCreateMutable(0, count, &kCFTypeArrayCallBacks));
     99     for (CFIndex i = 0; i < count; ++i) {
    100         CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(unfilteredCookies, i);
    101 
    102         // <rdar://problem/5632883> CFHTTPCookieStorage would store an empty cookie,
    103         // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent
    104         // that, but we also need to avoid sending cookies that were previously stored, and
    105         // there's no harm to doing this check because such a cookie is never valid.
    106         if (!CFStringGetLength(cookieName(cookie).get()))
    107             continue;
    108 
    109         if (CFHTTPCookieIsHTTPOnly(cookie))
    110             continue;
    111 
    112         CFArrayAppendValue(filteredCookies.get(), cookie);
    113     }
    114     return filteredCookies;
    115 }
    116 
    117 void setCookies(Document* document, const KURL& url, const String& value)
    118 {
    119     // <rdar://problem/5632883> CFHTTPCookieStorage stores an empty cookie, which would be sent as "Cookie: =".
    120     if (value.isEmpty())
    121         return;
    122 
    123     CFHTTPCookieStorageRef cookieStorage = currentCookieStorage();
    124     if (!cookieStorage)
    125         return;
    126 
    127     RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL());
    128     RetainPtr<CFURLRef> firstPartyForCookiesCF(AdoptCF, document->firstPartyForCookies().createCFURL());
    129 
    130     // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034>
    131     // cookiesWithResponseHeaderFields doesn't parse cookies without a value
    132     String cookieString = value.contains('=') ? value : value + "=";
    133 
    134     RetainPtr<CFStringRef> cookieStringCF(AdoptCF, cookieString.createCFString());
    135     RetainPtr<CFDictionaryRef> headerFieldsCF(AdoptCF, CFDictionaryCreate(kCFAllocatorDefault,
    136         (const void**)&s_setCookieKeyCF, (const void**)&cookieStringCF, 1,
    137         &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    138 
    139     RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieCreateWithResponseHeaderFields(kCFAllocatorDefault,
    140         headerFieldsCF.get(), urlCF.get()));
    141 
    142     CFHTTPCookieStorageSetCookies(cookieStorage, filterCookies(cookiesCF.get()).get(), urlCF.get(), firstPartyForCookiesCF.get());
    143 }
    144 
    145 String cookies(const Document* /*document*/, const KURL& url)
    146 {
    147     CFHTTPCookieStorageRef cookieStorage = currentCookieStorage();
    148     if (!cookieStorage)
    149         return String();
    150 
    151     RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL());
    152 
    153     bool secure = url.protocolIs("https");
    154     RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, urlCF.get(), secure));
    155     RetainPtr<CFDictionaryRef> headerCF(AdoptCF, CFHTTPCookieCopyRequestHeaderFields(kCFAllocatorDefault, filterCookies(cookiesCF.get()).get()));
    156     return (CFStringRef)CFDictionaryGetValue(headerCF.get(), s_cookieCF);
    157 }
    158 
    159 String cookieRequestHeaderFieldValue(const Document* /*document*/, const KURL& url)
    160 {
    161     CFHTTPCookieStorageRef cookieStorage = currentCookieStorage();
    162     if (!cookieStorage)
    163         return String();
    164 
    165     RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL());
    166 
    167     bool secure = url.protocolIs("https");
    168     RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, urlCF.get(), secure));
    169     RetainPtr<CFDictionaryRef> headerCF(AdoptCF, CFHTTPCookieCopyRequestHeaderFields(kCFAllocatorDefault, cookiesCF.get()));
    170     return (CFStringRef)CFDictionaryGetValue(headerCF.get(), s_cookieCF);
    171 }
    172 
    173 bool cookiesEnabled(const Document* /*document*/)
    174 {
    175     CFHTTPCookieStorageAcceptPolicy policy = CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain;
    176     if (CFHTTPCookieStorageRef cookieStorage = currentCookieStorage())
    177         policy = CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage);
    178     return policy == CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain || policy == CFHTTPCookieStorageAcceptPolicyAlways;
    179 }
    180 
    181 bool getRawCookies(const Document*, const KURL& url, Vector<Cookie>& rawCookies)
    182 {
    183     rawCookies.clear();
    184     CFHTTPCookieStorageRef cookieStorage = currentCookieStorage();
    185     if (!cookieStorage)
    186         return false;
    187 
    188     RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL());
    189 
    190     bool sendSecureCookies = url.protocolIs("https");
    191     RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, urlCF.get(), sendSecureCookies));
    192 
    193     CFIndex count = CFArrayGetCount(cookiesCF.get());
    194     rawCookies.reserveCapacity(count);
    195 
    196     for (CFIndex i = 0; i < count; i++) {
    197        CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(cookiesCF.get(), i);
    198        String name = cookieName(cookie).get();
    199        String value = cookieValue(cookie).get();
    200        String domain = cookieDomain(cookie).get();
    201        String path = cookiePath(cookie).get();
    202 
    203        double expires = (cookieExpirationTime(cookie) + kCFAbsoluteTimeIntervalSince1970) * 1000;
    204 
    205        bool httpOnly = CFHTTPCookieIsHTTPOnly(cookie);
    206        bool secure = CFHTTPCookieIsSecure(cookie);
    207        bool session = false;    // FIXME: Need API for if a cookie is a session cookie.
    208 
    209        rawCookies.uncheckedAppend(Cookie(name, value, domain, path, expires, httpOnly, secure, session));
    210     }
    211 
    212     return true;
    213 }
    214 
    215 void deleteCookie(const Document*, const KURL& url, const String& name)
    216 {
    217     CFHTTPCookieStorageRef cookieStorage = currentCookieStorage();
    218     if (!cookieStorage)
    219         return;
    220 
    221     RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL());
    222 
    223     bool sendSecureCookies = url.protocolIs("https");
    224     RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, urlCF.get(), sendSecureCookies));
    225 
    226     CFIndex count = CFArrayGetCount(cookiesCF.get());
    227     for (CFIndex i = 0; i < count; i++) {
    228         CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(cookiesCF.get(), i);
    229         if (String(cookieName(cookie).get()) == name) {
    230             CFHTTPCookieStorageDeleteCookie(cookieStorage, cookie);
    231             break;
    232         }
    233     }
    234 }
    235 
    236 void getHostnamesWithCookies(HashSet<String>& hostnames)
    237 {
    238     CFHTTPCookieStorageRef cookieStorage = currentCookieStorage();
    239     if (!cookieStorage)
    240         return;
    241 
    242     RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookies(cookieStorage));
    243     if (!cookiesCF)
    244         return;
    245 
    246     CFIndex count = CFArrayGetCount(cookiesCF.get());
    247     for (CFIndex i = 0; i < count; ++i) {
    248         CFHTTPCookieRef cookie = static_cast<CFHTTPCookieRef>(const_cast<void *>(CFArrayGetValueAtIndex(cookiesCF.get(), i)));
    249         RetainPtr<CFStringRef> domain = cookieDomain(cookie);
    250         hostnames.add(domain.get());
    251     }
    252 }
    253 
    254 void deleteCookiesForHostname(const String& hostname)
    255 {
    256     CFHTTPCookieStorageRef cookieStorage = currentCookieStorage();
    257     if (!cookieStorage)
    258         return;
    259 
    260     RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookies(cookieStorage));
    261     if (!cookiesCF)
    262         return;
    263 
    264     CFIndex count = CFArrayGetCount(cookiesCF.get());
    265     for (CFIndex i = count - 1; i >=0; i--) {
    266         CFHTTPCookieRef cookie = static_cast<CFHTTPCookieRef>(const_cast<void *>(CFArrayGetValueAtIndex(cookiesCF.get(), i)));
    267         RetainPtr<CFStringRef> domain = cookieDomain(cookie);
    268         if (String(domain.get()) == hostname)
    269             CFHTTPCookieStorageDeleteCookie(cookieStorage, cookie);
    270     }
    271 }
    272 
    273 void deleteAllCookies()
    274 {
    275     CFHTTPCookieStorageRef cookieStorage = currentCookieStorage();
    276     if (!cookieStorage)
    277         return;
    278 
    279     CFHTTPCookieStorageDeleteAllCookies(cookieStorage);
    280 }
    281 
    282 } // namespace WebCore
    283 
    284 #endif // USE(CFNETWORK)
    285