Remove Cocoa CFURLConnection loading code
[WebKit-https.git] / Source / WebCore / platform / network / mac / CookieJarMac.mm
1 /*
2  * Copyright (C) 2003-2017 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 #import "CookiesStrategy.h"
30 #import "NetworkStorageSession.h"
31 #import <pal/spi/cf/CFNetworkSPI.h>
32 #import <wtf/BlockObjCExceptions.h>
33
34 #import "Cookie.h"
35 #import "CookieStorage.h"
36 #import "URL.h"
37 #import <wtf/Optional.h>
38 #import <wtf/text/StringBuilder.h>
39
40 @interface NSURL ()
41 - (CFURLRef)_cfurl;
42 @end
43
44 namespace WebCore {
45
46 static NSArray *httpCookies(CFHTTPCookieStorageRef cookieStorage)
47 {
48     if (!cookieStorage)
49         return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
50     
51     auto cookies = adoptCF(CFHTTPCookieStorageCopyCookies(cookieStorage));
52     return [NSHTTPCookie _cf2nsCookies:cookies.get()];
53 }
54
55 static void deleteHTTPCookie(CFHTTPCookieStorageRef cookieStorage, NSHTTPCookie *cookie)
56 {
57     if (!cookieStorage) {
58         [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
59         return;
60     }
61     
62     CFHTTPCookieStorageDeleteCookie(cookieStorage, [cookie _GetInternalCFHTTPCookie]);
63 }
64
65 static NSArray *httpCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSURL *firstParty, NSURL *url)
66 {
67     if (!cookieStorage)
68         cookieStorage = _CFHTTPCookieStorageGetDefault(kCFAllocatorDefault);
69
70     bool secure = ![[url scheme] caseInsensitiveCompare:@"https"];
71     
72     auto cookies = adoptCF(_CFHTTPCookieStorageCopyCookiesForURLWithMainDocumentURL(cookieStorage, static_cast<CFURLRef>(url), static_cast<CFURLRef>(firstParty), secure));
73     NSArray *nsCookies = [NSHTTPCookie _cf2nsCookies:cookies.get()];
74
75     return nsCookies;
76 }
77
78     
79 static RetainPtr<NSArray> filterCookies(NSArray *unfilteredCookies)
80 {
81     NSUInteger count = [unfilteredCookies count];
82     RetainPtr<NSMutableArray> filteredCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:count]);
83
84     for (NSUInteger i = 0; i < count; ++i) {
85         NSHTTPCookie *cookie = (NSHTTPCookie *)[unfilteredCookies objectAtIndex:i];
86
87         // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
88         // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent
89         // that, but we also need to avoid sending cookies that were previously stored, and
90         // there's no harm to doing this check because such a cookie is never valid.
91         if (![[cookie name] length])
92             continue;
93
94         if ([cookie isHTTPOnly])
95             continue;
96
97         [filteredCookies.get() addObject:cookie];
98     }
99
100     return filteredCookies;
101 }
102
103 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
104
105 static NSArray *applyPartitionToCookies(NSString *partition, NSArray *cookies)
106 {
107     // FIXME 24747739: CFNetwork should expose this key as SPI
108     static NSString * const partitionKey = @"StoragePartition";
109
110     NSMutableArray *partitionedCookies = [NSMutableArray arrayWithCapacity:cookies.count];
111     for (NSHTTPCookie *cookie in cookies) {
112         RetainPtr<NSMutableDictionary> properties = adoptNS([cookie.properties mutableCopy]);
113         [properties setObject:partition forKey:partitionKey];
114         [partitionedCookies addObject:[NSHTTPCookie cookieWithProperties:properties.get()]];
115     }
116
117     return partitionedCookies;
118 }
119
120 static bool cookiesAreBlockedForURL(const NetworkStorageSession& session, const URL& firstParty, const URL& url)
121 {
122     return session.shouldBlockCookies(firstParty, url);
123 }
124
125 static NSArray *cookiesInPartitionForURL(const NetworkStorageSession& session, const URL& firstParty, const URL& url)
126 {
127     String partition = session.cookieStoragePartition(firstParty, url);
128     if (partition.isEmpty())
129         return nil;
130
131     // FIXME: Stop creating a new NSHTTPCookieStorage object each time we want to query the cookie jar.
132     // NetworkStorageSession could instead keep a NSHTTPCookieStorage object for us.
133     RetainPtr<NSHTTPCookieStorage> cookieStorage;
134     if (auto storage = session.cookieStorage())
135         cookieStorage = adoptNS([[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage.get()]);
136     else
137         cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
138
139     // The _getCookiesForURL: method calls the completionHandler synchronously.
140     std::optional<RetainPtr<NSArray *>> cookiesPtr;
141     [cookieStorage _getCookiesForURL:url mainDocumentURL:firstParty partition:partition completionHandler:[&cookiesPtr](NSArray *cookies) {
142         cookiesPtr = retainPtr(cookies);
143     }];
144     ASSERT(!!cookiesPtr);
145
146     return cookiesPtr->autorelease();
147 }
148
149 #endif // HAVE(CFNETWORK_STORAGE_PARTITIONING)
150     
151 static NSArray *cookiesForURL(const NetworkStorageSession& session, const URL& firstParty, const URL& url)
152 {
153 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
154     if (cookiesAreBlockedForURL(session, firstParty, url))
155         return nil;
156     
157     if (NSArray *cookies = cookiesInPartitionForURL(session, firstParty, url))
158         return cookies;
159 #endif
160     return httpCookiesForURL(session.cookieStorage().get(), firstParty, url);
161 }
162
163 enum IncludeHTTPOnlyOrNot { DoNotIncludeHTTPOnly, IncludeHTTPOnly };
164 static std::pair<String, bool> cookiesForSession(const NetworkStorageSession& session, const URL& firstParty, const URL& url, IncludeHTTPOnlyOrNot includeHTTPOnly, IncludeSecureCookies includeSecureCookies)
165 {
166     BEGIN_BLOCK_OBJC_EXCEPTIONS;
167
168     NSArray *cookies = cookiesForURL(session, firstParty, url);
169     if (![cookies count])
170         return { String(), false }; // Return a null string, not an empty one that StringBuilder would create below.
171
172     StringBuilder cookiesBuilder;
173     bool didAccessSecureCookies = false;
174     for (NSHTTPCookie *cookie in cookies) {
175         if (![[cookie name] length])
176             continue;
177
178         if (!includeHTTPOnly && [cookie isHTTPOnly])
179             continue;
180
181         if ([cookie isSecure]) {
182             didAccessSecureCookies = true;
183             if (includeSecureCookies == IncludeSecureCookies::No)
184                 continue;
185         }
186
187         if (!cookiesBuilder.isEmpty())
188             cookiesBuilder.appendLiteral("; ");
189
190         cookiesBuilder.append([cookie name]);
191         cookiesBuilder.append('=');
192         cookiesBuilder.append([cookie value]);
193     }
194     return { cookiesBuilder.toString(), didAccessSecureCookies };
195
196     END_BLOCK_OBJC_EXCEPTIONS;
197     return { String(), false };
198 }
199
200 static void setHTTPCookiesForURL(CFHTTPCookieStorageRef cookieStorage, NSArray *cookies, NSURL *url, NSURL *mainDocumentURL)
201 {
202     if (!cookieStorage) {
203         [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:mainDocumentURL];
204         return;
205     }
206
207     auto cfCookies = adoptCF([NSHTTPCookie _ns2cfCookies:cookies]);
208     CFHTTPCookieStorageSetCookies(cookieStorage, cfCookies.get(), [url _cfurl], [mainDocumentURL _cfurl]);
209 }
210
211 static void deleteAllHTTPCookies(CFHTTPCookieStorageRef cookieStorage)
212 {
213     if (!cookieStorage) {
214         NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
215         NSArray *cookies = [cookieStorage cookies];
216         if (!cookies)
217             return;
218
219         for (NSHTTPCookie *cookie in cookies)
220             [cookieStorage deleteCookie:cookie];
221         return;
222     }
223
224     CFHTTPCookieStorageDeleteAllCookies(cookieStorage);
225 }
226
227 std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, IncludeSecureCookies includeSecureCookies)
228 {
229     return cookiesForSession(session, firstParty, url, DoNotIncludeHTTPOnly, includeSecureCookies);
230 }
231
232 std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const URL& url, IncludeSecureCookies includeSecureCookies)
233 {
234     return cookiesForSession(session, firstParty, url, IncludeHTTPOnly, includeSecureCookies);
235 }
236
237 void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, const String& cookieStr)
238 {
239     BEGIN_BLOCK_OBJC_EXCEPTIONS;
240
241     // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would store an empty cookie,
242     // which would be sent as "Cookie: =".
243     if (cookieStr.isEmpty())
244         return;
245
246     // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034>
247     // cookiesWithResponseHeaderFields doesn't parse cookies without a value
248     String cookieString = cookieStr.contains('=') ? cookieStr : cookieStr + "=";
249
250     NSURL *cookieURL = url;
251     NSDictionary *headerFields = [NSDictionary dictionaryWithObject:cookieString forKey:@"Set-Cookie"];
252
253 #if PLATFORM(MAC)
254     NSArray *unfilteredCookies = [NSHTTPCookie _parsedCookiesWithResponseHeaderFields:headerFields forURL:cookieURL];
255 #else
256     NSArray *unfilteredCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:cookieURL];
257 #endif
258
259     RetainPtr<NSArray> filteredCookies = filterCookies(unfilteredCookies);
260     ASSERT([filteredCookies.get() count] <= 1);
261
262 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
263     String partition = session.cookieStoragePartition(firstParty, url);
264     if (!partition.isEmpty())
265         filteredCookies = applyPartitionToCookies(partition, filteredCookies.get());
266 #endif
267
268     setHTTPCookiesForURL(session.cookieStorage().get(), filteredCookies.get(), cookieURL, firstParty);
269
270     END_BLOCK_OBJC_EXCEPTIONS;
271 }
272
273 static NSHTTPCookieAcceptPolicy httpCookieAcceptPolicy(CFHTTPCookieStorageRef cookieStorage)
274 {
275     if (!cookieStorage)
276         return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookieAcceptPolicy];
277
278     return static_cast<NSHTTPCookieAcceptPolicy>(CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage));
279 }
280
281 bool cookiesEnabled(const NetworkStorageSession& session)
282 {
283     BEGIN_BLOCK_OBJC_EXCEPTIONS;
284
285     NSHTTPCookieAcceptPolicy cookieAcceptPolicy = httpCookieAcceptPolicy(session.cookieStorage().get());
286     return cookieAcceptPolicy == NSHTTPCookieAcceptPolicyAlways || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain || cookieAcceptPolicy == NSHTTPCookieAcceptPolicyExclusivelyFromMainDocumentDomain;
287
288     END_BLOCK_OBJC_EXCEPTIONS;
289     return false;
290 }
291
292 bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const URL& url, Vector<Cookie>& rawCookies)
293 {
294     rawCookies.clear();
295     BEGIN_BLOCK_OBJC_EXCEPTIONS;
296
297     NSArray *cookies = cookiesForURL(session, firstParty, url);
298     NSUInteger count = [cookies count];
299     rawCookies.reserveCapacity(count);
300     for (NSUInteger i = 0; i < count; ++i) {
301         NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
302         rawCookies.uncheckedAppend({ cookie });
303     }
304
305     END_BLOCK_OBJC_EXCEPTIONS;
306     return true;
307 }
308
309 void deleteCookie(const NetworkStorageSession& session, const URL& url, const String& cookieName)
310 {
311     BEGIN_BLOCK_OBJC_EXCEPTIONS;
312
313     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = session.cookieStorage();
314     NSArray *cookies = httpCookiesForURL(cookieStorage.get(), nil, url);
315
316     NSString *cookieNameString = cookieName;
317
318     NSUInteger count = [cookies count];
319     for (NSUInteger i = 0; i < count; ++i) {
320         NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
321         if ([[cookie name] isEqualToString:cookieNameString])
322             deleteHTTPCookie(cookieStorage.get(), cookie);
323     }
324
325     END_BLOCK_OBJC_EXCEPTIONS;
326 }
327
328 void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames)
329 {
330     BEGIN_BLOCK_OBJC_EXCEPTIONS;
331
332     NSArray *cookies = httpCookies(session.cookieStorage().get());
333     
334     for (NSHTTPCookie* cookie in cookies)
335         hostnames.add([cookie domain]);
336     
337     END_BLOCK_OBJC_EXCEPTIONS;
338 }
339
340 void deleteAllCookies(const NetworkStorageSession& session)
341 {
342     deleteAllHTTPCookies(session.cookieStorage().get());
343 }
344
345 void deleteCookiesForHostnames(const NetworkStorageSession& session, const Vector<String>& hostnames)
346 {
347     BEGIN_BLOCK_OBJC_EXCEPTIONS;
348
349     RetainPtr<CFHTTPCookieStorageRef> cookieStorage = session.cookieStorage();
350     NSArray *cookies = httpCookies(cookieStorage.get());
351     if (!cookies)
352         return;
353
354     HashMap<String, Vector<RetainPtr<NSHTTPCookie>>> cookiesByDomain;
355     for (NSHTTPCookie* cookie in cookies) {
356         auto& cookies = cookiesByDomain.add(cookie.domain, Vector<RetainPtr<NSHTTPCookie>>()).iterator->value;
357         cookies.append(cookie);
358     }
359
360     for (const auto& hostname : hostnames) {
361         auto it = cookiesByDomain.find(hostname);
362         if (it == cookiesByDomain.end())
363             continue;
364
365         for (auto& cookie : it->value)
366             deleteHTTPCookie(cookieStorage.get(), cookie.get());
367     }
368
369     [session.nsCookieStorage() _saveCookies];
370
371     END_BLOCK_OBJC_EXCEPTIONS;
372 }
373
374 void deleteAllCookiesModifiedSince(const NetworkStorageSession& session, std::chrono::system_clock::time_point timePoint)
375 {
376     if (![NSHTTPCookieStorage instancesRespondToSelector:@selector(removeCookiesSinceDate:)])
377         return;
378
379     NSTimeInterval timeInterval = std::chrono::duration_cast<std::chrono::duration<double>>(timePoint.time_since_epoch()).count();
380     NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
381
382     auto *storage = session.nsCookieStorage();
383
384     [storage removeCookiesSinceDate:date];
385     [storage _saveCookies];
386 }
387
388 }