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