1/*
2 * Copyright (C) 2003, 2006, 2008, 2012 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 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 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 "PlatformCookieJar.h"
28
29#if !USE(CFNETWORK)
30
31#import "BlockExceptions.h"
32#import "Cookie.h"
33#import "CookieStorage.h"
34#import "URL.h"
35#import "NetworkStorageSession.h"
36#import "WebCoreSystemInterface.h"
37
38enum {
39    NSHTTPCookieAcceptPolicyExclusivelyFromMainDocumentDomain = 3
40};
41
42@interface NSHTTPCookieStorage (Details)
43- (void)removeCookiesSinceDate:(NSDate *)date;
44@end
45
46namespace WebCore {
47
48static RetainPtr<NSArray> filterCookies(NSArray *unfilteredCookies)
49{
50    NSUInteger count = [unfilteredCookies count];
51    RetainPtr<NSMutableArray> filteredCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:count]);
52
53    for (NSUInteger i = 0; i < count; ++i) {
54        NSHTTPCookie *cookie = (NSHTTPCookie *)[unfilteredCookies objectAtIndex:i];
55
56        // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
57        // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent
58        // that, but we also need to avoid sending cookies that were previously stored, and
59        // there's no harm to doing this check because such a cookie is never valid.
60        if (![[cookie name] length])
61            continue;
62
63        if ([cookie isHTTPOnly])
64            continue;
65
66        [filteredCookies.get() addObject:cookie];
67    }
68
69    return filteredCookies;
70}
71
72String cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url)
73{
74    BEGIN_BLOCK_OBJC_EXCEPTIONS;
75
76    NSArray *cookies = wkHTTPCookiesForURL(session.cookieStorage().get(), firstParty, url);
77    return [[NSHTTPCookie requestHeaderFieldsWithCookies:filterCookies(cookies).get()] objectForKey:@"Cookie"];
78
79    END_BLOCK_OBJC_EXCEPTIONS;
80    return String();
81}
82
83String cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url)
84{
85    BEGIN_BLOCK_OBJC_EXCEPTIONS;
86
87    NSArray *cookies = wkHTTPCookiesForURL(session.cookieStorage().get(), firstParty, url);
88    return [[NSHTTPCookie requestHeaderFieldsWithCookies:cookies] objectForKey:@"Cookie"];
89
90    END_BLOCK_OBJC_EXCEPTIONS;
91    return String();
92}
93
94void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, const String& cookieStr)
95{
96    BEGIN_BLOCK_OBJC_EXCEPTIONS;
97
98    // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
99    // which would be sent as "Cookie: =".
100    if (cookieStr.isEmpty())
101        return;
102
103    // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034>
104    // cookiesWithResponseHeaderFields doesn't parse cookies without a value
105    String cookieString = cookieStr.contains('=') ? cookieStr : cookieStr + "=";
106
107    NSURL *cookieURL = url;
108    RetainPtr<NSArray> filteredCookies = filterCookies([NSHTTPCookie cookiesWithResponseHeaderFields:[NSDictionary dictionaryWithObject:cookieString forKey:@"Set-Cookie"] forURL:cookieURL]);
109    ASSERT([filteredCookies.get() count] <= 1);
110
111    wkSetHTTPCookiesForURL(session.cookieStorage().get(), filteredCookies.get(), cookieURL, firstParty);
112
113    END_BLOCK_OBJC_EXCEPTIONS;
114}
115
116bool cookiesEnabled(const NetworkStorageSession& session, const URL& /*firstParty*/, const URL& /*url*/)
117{
118    BEGIN_BLOCK_OBJC_EXCEPTIONS;
119
120    NSHTTPCookieAcceptPolicy cookieAcceptPolicy = static_cast<NSHTTPCookieAcceptPolicy>(wkGetHTTPCookieAcceptPolicy(session.cookieStorage().get()));
121    return cookieAcceptPolicy == NSHTTPCookieAcceptPolicyAlways || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyExclusivelyFromMainDocumentDomain;
122
123    END_BLOCK_OBJC_EXCEPTIONS;
124    return false;
125}
126
127bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, Vector<Cookie>& rawCookies)
128{
129    rawCookies.clear();
130    BEGIN_BLOCK_OBJC_EXCEPTIONS;
131
132    NSArray *cookies = wkHTTPCookiesForURL(session.cookieStorage().get(), firstParty, url);
133    NSUInteger count = [cookies count];
134    rawCookies.reserveCapacity(count);
135    for (NSUInteger i = 0; i < count; ++i) {
136        NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
137        NSTimeInterval expires = [[cookie expiresDate] timeIntervalSince1970] * 1000;
138        rawCookies.uncheckedAppend(Cookie([cookie name], [cookie value], [cookie domain], [cookie path], expires,
139            [cookie isHTTPOnly], [cookie isSecure], [cookie isSessionOnly]));
140    }
141
142    END_BLOCK_OBJC_EXCEPTIONS;
143    return true;
144}
145
146void deleteCookie(const NetworkStorageSession& session, const URL& url, const String& cookieName)
147{
148    BEGIN_BLOCK_OBJC_EXCEPTIONS;
149
150    RetainPtr<CFHTTPCookieStorageRef> cookieStorage = session.cookieStorage();
151    NSArray *cookies = wkHTTPCookiesForURL(cookieStorage.get(), 0, url);
152
153    NSString *cookieNameString = cookieName;
154
155    NSUInteger count = [cookies count];
156    for (NSUInteger i = 0; i < count; ++i) {
157        NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
158        if ([[cookie name] isEqualToString:cookieNameString])
159            wkDeleteHTTPCookie(cookieStorage.get(), cookie);
160    }
161
162    END_BLOCK_OBJC_EXCEPTIONS;
163}
164
165void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames)
166{
167    BEGIN_BLOCK_OBJC_EXCEPTIONS;
168
169    NSArray *cookies = wkHTTPCookies(session.cookieStorage().get());
170
171    for (NSHTTPCookie* cookie in cookies)
172        hostnames.add([cookie domain]);
173
174    END_BLOCK_OBJC_EXCEPTIONS;
175}
176
177void deleteCookiesForHostname(const NetworkStorageSession& session, const String& hostname)
178{
179    BEGIN_BLOCK_OBJC_EXCEPTIONS;
180
181    RetainPtr<CFHTTPCookieStorageRef> cookieStorage = session.cookieStorage();
182    NSArray *cookies = wkHTTPCookies(cookieStorage.get());
183    if (!cookies)
184        return;
185
186    for (NSHTTPCookie* cookie in cookies) {
187        if (hostname == String([cookie domain]))
188            wkDeleteHTTPCookie(cookieStorage.get(), cookie);
189    }
190
191    END_BLOCK_OBJC_EXCEPTIONS;
192}
193
194void deleteAllCookies(const NetworkStorageSession& session)
195{
196    wkDeleteAllHTTPCookies(session.cookieStorage().get());
197}
198
199void deleteAllCookiesModifiedAfterDate(const NetworkStorageSession& session, double date)
200{
201    UNUSED_PARAM(session);
202
203    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
204    if ([cookieStorage respondsToSelector:@selector(removeCookiesSinceDate:)])
205        [cookieStorage removeCookiesSinceDate:[NSDate dateWithTimeIntervalSince1970:date]];
206}
207
208}
209
210#endif // !USE(CFNETWORK)
211