1 /* 2 * Copyright (C) 2003, 2006, 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 #import "config.h" 27 #import "CookieJar.h" 28 29 #import "BlockExceptions.h" 30 #import "Cookie.h" 31 #import "CookieStorage.h" 32 #import "Document.h" 33 #import "KURL.h" 34 #import "WebCoreSystemInterface.h" 35 #import <wtf/RetainPtr.h> 36 37 #ifdef BUILDING_ON_TIGER 38 typedef unsigned NSUInteger; 39 #endif 40 41 @interface NSHTTPCookie (WebCoreHTTPOnlyCookies) 42 - (BOOL)isHTTPOnly; 43 @end 44 45 namespace WebCore { 46 47 static bool isHTTPOnly(NSHTTPCookie *cookie) 48 { 49 // Once we require a newer version of Foundation with the isHTTPOnly method, 50 // we can eliminate the instancesRespondToSelector: check. 51 static bool supportsHTTPOnlyCookies = [NSHTTPCookie instancesRespondToSelector:@selector(isHTTPOnly)]; 52 return supportsHTTPOnlyCookies && [cookie isHTTPOnly]; 53 } 54 55 static RetainPtr<NSArray> filterCookies(NSArray *unfilteredCookies) 56 { 57 NSUInteger count = [unfilteredCookies count]; 58 RetainPtr<NSMutableArray> filteredCookies(AdoptNS, [[NSMutableArray alloc] initWithCapacity:count]); 59 60 for (NSUInteger i = 0; i < count; ++i) { 61 NSHTTPCookie *cookie = (NSHTTPCookie *)[unfilteredCookies objectAtIndex:i]; 62 63 // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie, 64 // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent 65 // that, but we also need to avoid sending cookies that were previously stored, and 66 // there's no harm to doing this check because such a cookie is never valid. 67 if (![[cookie name] length]) 68 continue; 69 70 if (isHTTPOnly(cookie)) 71 continue; 72 73 [filteredCookies.get() addObject:cookie]; 74 } 75 76 return filteredCookies; 77 } 78 79 String cookies(const Document*, const KURL& url) 80 { 81 BEGIN_BLOCK_OBJC_EXCEPTIONS; 82 83 NSURL *cookieURL = url; 84 NSArray *cookies; 85 #if USE(CFURLSTORAGESESSIONS) 86 if (CFHTTPCookieStorageRef cookieStorage = privateBrowsingCookieStorage().get()) 87 cookies = wkHTTPCookiesForURL(cookieStorage, cookieURL); 88 else 89 #endif 90 cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:cookieURL]; 91 92 return [[NSHTTPCookie requestHeaderFieldsWithCookies:filterCookies(cookies).get()] objectForKey:@"Cookie"]; 93 94 END_BLOCK_OBJC_EXCEPTIONS; 95 return String(); 96 } 97 98 String cookieRequestHeaderFieldValue(const Document*, const KURL& url) 99 { 100 BEGIN_BLOCK_OBJC_EXCEPTIONS; 101 102 NSURL *cookieURL = url; 103 NSArray *cookies; 104 #if USE(CFURLSTORAGESESSIONS) 105 if (CFHTTPCookieStorageRef cookieStorage = privateBrowsingCookieStorage().get()) 106 cookies = wkHTTPCookiesForURL(cookieStorage, cookieURL); 107 else 108 #endif 109 cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:cookieURL]; 110 111 return [[NSHTTPCookie requestHeaderFieldsWithCookies:cookies] objectForKey:@"Cookie"]; 112 113 END_BLOCK_OBJC_EXCEPTIONS; 114 return String(); 115 } 116 117 void setCookies(Document* document, const KURL& url, const String& cookieStr) 118 { 119 BEGIN_BLOCK_OBJC_EXCEPTIONS; 120 121 // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie, 122 // which would be sent as "Cookie: =". 123 if (cookieStr.isEmpty()) 124 return; 125 126 // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034> 127 // cookiesWithResponseHeaderFields doesn't parse cookies without a value 128 String cookieString = cookieStr.contains('=') ? cookieStr : cookieStr + "="; 129 130 NSURL *cookieURL = url; 131 RetainPtr<NSArray> filteredCookies = filterCookies([NSHTTPCookie cookiesWithResponseHeaderFields:[NSDictionary dictionaryWithObject:cookieString forKey:@"Set-Cookie"] forURL:cookieURL]); 132 133 #if USE(CFURLSTORAGESESSIONS) 134 if (CFHTTPCookieStorageRef cookieStorage = privateBrowsingCookieStorage().get()) 135 wkSetHTTPCookiesForURL(cookieStorage, filteredCookies.get(), cookieURL, document->firstPartyForCookies()); 136 else 137 #endif 138 [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:filteredCookies.get() forURL:cookieURL mainDocumentURL:document->firstPartyForCookies()]; 139 140 END_BLOCK_OBJC_EXCEPTIONS; 141 } 142 143 bool cookiesEnabled(const Document*) 144 { 145 BEGIN_BLOCK_OBJC_EXCEPTIONS; 146 147 NSHTTPCookieAcceptPolicy cookieAcceptPolicy; 148 #if USE(CFURLSTORAGESESSIONS) 149 if (CFHTTPCookieStorageRef cookieStorage = privateBrowsingCookieStorage().get()) 150 cookieAcceptPolicy = wkGetHTTPCookieAcceptPolicy(cookieStorage); 151 else 152 #endif 153 cookieAcceptPolicy = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy]; 154 155 return cookieAcceptPolicy == NSHTTPCookieAcceptPolicyAlways || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain; 156 157 END_BLOCK_OBJC_EXCEPTIONS; 158 return false; 159 } 160 161 bool getRawCookies(const Document*, const KURL& url, Vector<Cookie>& rawCookies) 162 { 163 rawCookies.clear(); 164 BEGIN_BLOCK_OBJC_EXCEPTIONS; 165 166 NSURL *cookieURL = url; 167 NSArray *cookies; 168 #if USE(CFURLSTORAGESESSIONS) 169 if (CFHTTPCookieStorageRef cookieStorage = privateBrowsingCookieStorage().get()) 170 cookies = wkHTTPCookiesForURL(cookieStorage, cookieURL); 171 else 172 #endif 173 cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:cookieURL]; 174 175 NSUInteger count = [cookies count]; 176 rawCookies.reserveCapacity(count); 177 for (NSUInteger i = 0; i < count; ++i) { 178 NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i]; 179 NSString *name = [cookie name]; 180 NSString *value = [cookie value]; 181 NSString *domain = [cookie domain]; 182 NSString *path = [cookie path]; 183 NSTimeInterval expires = [[cookie expiresDate] timeIntervalSince1970] * 1000; 184 bool httpOnly = [cookie isHTTPOnly]; 185 bool secure = [cookie isSecure]; 186 bool session = [cookie isSessionOnly]; 187 rawCookies.uncheckedAppend(Cookie(name, value, domain, path, expires, httpOnly, secure, session)); 188 } 189 190 END_BLOCK_OBJC_EXCEPTIONS; 191 return true; 192 } 193 194 void deleteCookie(const Document*, const KURL& url, const String& cookieName) 195 { 196 BEGIN_BLOCK_OBJC_EXCEPTIONS; 197 198 NSURL *cookieURL = url; 199 NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 200 NSArray *cookies; 201 #if USE(CFURLSTORAGESESSIONS) 202 CFHTTPCookieStorageRef cfCookieStorage = privateBrowsingCookieStorage().get(); 203 if (cfCookieStorage) 204 cookies = wkHTTPCookiesForURL(cfCookieStorage, cookieURL); 205 else 206 #endif 207 cookies = [cookieStorage cookiesForURL:cookieURL]; 208 209 NSString *cookieNameString = (NSString *) cookieName; 210 211 NSUInteger count = [cookies count]; 212 for (NSUInteger i = 0; i < count; ++i) { 213 NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i]; 214 if ([[cookie name] isEqualToString:cookieNameString]) { 215 #if USE(CFURLSTORAGESESSIONS) 216 if (cfCookieStorage) 217 wkDeleteHTTPCookie(cfCookieStorage, cookie); 218 else 219 #endif 220 [cookieStorage deleteCookie:cookie]; 221 break; 222 } 223 } 224 225 END_BLOCK_OBJC_EXCEPTIONS; 226 } 227 228 void getHostnamesWithCookies(HashSet<String>& hostnames) 229 { 230 BEGIN_BLOCK_OBJC_EXCEPTIONS; 231 232 NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 233 NSArray *cookies = [cookieStorage cookies]; 234 235 for (NSHTTPCookie* cookie in cookies) 236 hostnames.add([cookie domain]); 237 238 END_BLOCK_OBJC_EXCEPTIONS; 239 } 240 241 void deleteCookiesForHostname(const String& hostname) 242 { 243 BEGIN_BLOCK_OBJC_EXCEPTIONS; 244 245 NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 246 NSArray *cookies = [cookieStorage cookies]; 247 if (!cookies) 248 return; 249 250 for (NSHTTPCookie* cookie in cookies) { 251 if (hostname == String([cookie domain])) 252 [cookieStorage deleteCookie:cookie]; 253 } 254 255 END_BLOCK_OBJC_EXCEPTIONS; 256 } 257 258 void deleteAllCookies() 259 { 260 BEGIN_BLOCK_OBJC_EXCEPTIONS; 261 262 NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 263 NSArray *cookies = [cookieStorage cookies]; 264 if (!cookies) 265 return; 266 267 for (NSHTTPCookie* cookie in cookies) 268 [cookieStorage deleteCookie:cookie]; 269 270 END_BLOCK_OBJC_EXCEPTIONS; 271 } 272 273 } 274