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
cookieDomain(CFHTTPCookieRef cookie)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
cookieExpirationTime(CFHTTPCookieRef cookie)67 static inline CFAbsoluteTime cookieExpirationTime(CFHTTPCookieRef cookie)
68 {
69 if (CFHTTPCookieGetExpirationTimePtr())
70 return CFHTTPCookieGetExpirationTimePtr()(cookie);
71 return CFDateGetAbsoluteTime(CFHTTPCookieGetExpiratonDate(cookie));
72 }
73
cookieName(CFHTTPCookieRef cookie)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
cookiePath(CFHTTPCookieRef cookie)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
cookieValue(CFHTTPCookieRef cookie)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
filterCookies(CFArrayRef unfilteredCookies)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
setCookies(Document * document,const KURL & url,const String & value)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
cookies(const Document *,const KURL & url)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
cookieRequestHeaderFieldValue(const Document *,const KURL & url)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
cookiesEnabled(const Document *)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
getRawCookies(const Document *,const KURL & url,Vector<Cookie> & rawCookies)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
deleteCookie(const Document *,const KURL & url,const String & name)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
getHostnamesWithCookies(HashSet<String> & hostnames)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
deleteCookiesForHostname(const String & hostname)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
deleteAllCookies()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