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
38typedef unsigned NSUInteger;
39#endif
40
41@interface NSHTTPCookie (WebCoreHTTPOnlyCookies)
42- (BOOL)isHTTPOnly;
43@end
44
45namespace WebCore {
46
47static 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
55static 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
79String 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
98String 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
117void 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
143bool 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
161bool 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
194void 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
228void 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
241void 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
258void 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