dbcc9c83647d969fadbb4e6219b9254bda86628d
[WebKit.git] / WebCore / platform / network / cf / ResourceHandleCFNet.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2009 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
28 #include "ResourceHandle.h"
29 #include "ResourceHandleClient.h"
30 #include "ResourceHandleInternal.h"
31
32 #include "AuthenticationCF.h"
33 #include "AuthenticationChallenge.h"
34 #include "CookieStorageWin.h"
35 #include "CString.h"
36 #include "DocLoader.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "Logging.h"
40 #include "MIMETypeRegistry.h"
41 #include "ResourceError.h"
42 #include "ResourceResponse.h"
43
44 #include <wtf/HashMap.h>
45 #include <wtf/Threading.h>
46
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <process.h> // for _beginthread()
50
51 #include <CFNetwork/CFNetwork.h>
52 #include <WebKitSystemInterface/WebKitSystemInterface.h>
53
54 namespace WebCore {
55
56 static CFStringRef WebCoreSynchronousLoaderRunLoopMode = CFSTR("WebCoreSynchronousLoaderRunLoopMode");
57
58 class WebCoreSynchronousLoader {
59 public:
60     static RetainPtr<CFDataRef> load(const ResourceRequest&, StoredCredentials, ResourceResponse&, ResourceError&);
61
62 private:
63     WebCoreSynchronousLoader(ResourceResponse& response, ResourceError& error)
64         : m_isDone(false)
65         , m_response(response)
66         , m_error(error)
67     {
68     }
69
70     static CFURLRequestRef willSendRequest(CFURLConnectionRef, CFURLRequestRef, CFURLResponseRef, const void* clientInfo);
71     static void didReceiveResponse(CFURLConnectionRef, CFURLResponseRef, const void* clientInfo);
72     static void didReceiveData(CFURLConnectionRef, CFDataRef, CFIndex, const void* clientInfo);
73     static void didFinishLoading(CFURLConnectionRef, const void* clientInfo);
74     static void didFail(CFURLConnectionRef, CFErrorRef, const void* clientInfo);
75     static void didReceiveChallenge(CFURLConnectionRef, CFURLAuthChallengeRef, const void* clientInfo);
76     static Boolean shouldUseCredentialStorage(CFURLConnectionRef, const void* clientInfo);
77
78     bool m_isDone;
79     RetainPtr<CFURLRef> m_url;
80     RetainPtr<CFStringRef> m_user;
81     RetainPtr<CFStringRef> m_pass;
82     bool m_allowStoredCredentials;
83     ResourceResponse& m_response;
84     RetainPtr<CFMutableDataRef> m_data;
85     ResourceError& m_error;
86 };
87
88 static HashSet<String>& allowsAnyHTTPSCertificateHosts()
89 {
90     static HashSet<String> hosts;
91
92     return hosts;
93 }
94
95 static HashMap<String, RetainPtr<CFDataRef> >& clientCerts()
96 {
97     static HashMap<String, RetainPtr<CFDataRef> > certs;
98     return certs;
99 }
100
101 static void setDefaultMIMEType(CFURLResponseRef response)
102 {
103     static CFStringRef defaultMIMETypeString = defaultMIMEType().createCFString();
104     
105     CFURLResponseSetMIMEType(response, defaultMIMETypeString);
106 }
107
108 CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo)
109 {
110     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
111
112     if (!cfRedirectResponse) {
113         CFRetain(cfRequest);
114         return cfRequest;
115     }
116
117     LOG(Network, "CFNet - willSendRequest(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
118
119     ResourceRequest request(cfRequest);
120     handle->willSendRequest(request, cfRedirectResponse);
121
122     if (request.isNull())
123         return 0;
124
125     cfRequest = request.cfURLRequest();
126
127     CFRetain(cfRequest);
128     return cfRequest;
129 }
130
131 void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo) 
132 {
133     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
134
135     LOG(Network, "CFNet - didReceiveResponse(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
136
137     if (!handle->client())
138         return;
139
140     if (!CFURLResponseGetMIMEType(cfResponse)) {
141         // We should never be applying the default MIMEType if we told the networking layer to do content sniffing for handle.
142         ASSERT(!handle->shouldContentSniff());
143         setDefaultMIMEType(cfResponse);
144     }
145     
146     handle->client()->didReceiveResponse(handle, cfResponse);
147 }
148
149 void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo) 
150 {
151     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
152     const UInt8* bytes = CFDataGetBytePtr(data);
153     CFIndex length = CFDataGetLength(data);
154
155     LOG(Network, "CFNet - didReceiveData(conn=%p, handle=%p, bytes=%d) (%s)", conn, handle, length, handle->request().url().string().utf8().data());
156
157     if (handle->client())
158         handle->client()->didReceiveData(handle, (const char*)bytes, length, originalLength);
159 }
160
161 static void didSendBodyData(CFURLConnectionRef conn, CFIndex bytesWritten, CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite, const void *clientInfo)
162 {
163     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
164     if (!handle || !handle->client())
165         return;
166     handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite);
167 }
168
169 static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const void* clientInfo)
170 {
171     ResourceHandle* handle = const_cast<ResourceHandle*>(static_cast<const ResourceHandle*>(clientInfo));
172
173     LOG(Network, "CFNet - shouldUseCredentialStorage(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
174
175     if (!handle)
176         return false;
177
178     return handle->shouldUseCredentialStorage();
179 }
180
181 void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo) 
182 {
183     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
184
185     LOG(Network, "CFNet - didFinishLoading(conn=%p, handle=%p) (%s)", conn, handle, handle->request().url().string().utf8().data());
186
187     if (handle->client())
188         handle->client()->didFinishLoading(handle);
189 }
190
191 void didFail(CFURLConnectionRef conn, CFErrorRef error, const void* clientInfo) 
192 {
193     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
194
195     LOG(Network, "CFNet - didFail(conn=%p, handle=%p, error = %p) (%s)", conn, handle, error, handle->request().url().string().utf8().data());
196
197     if (handle->client())
198         handle->client()->didFail(handle, ResourceError(error));
199 }
200
201 CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef conn, CFCachedURLResponseRef cachedResponse, const void* clientInfo) 
202 {
203     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
204
205     if (handle->client() && !handle->client()->shouldCacheResponse(handle, cachedResponse))
206         return 0;
207
208     CacheStoragePolicy policy = static_cast<CacheStoragePolicy>(CFCachedURLResponseGetStoragePolicy(cachedResponse));
209
210     if (handle->client())
211         handle->client()->willCacheResponse(handle, policy);
212
213     if (static_cast<CFURLCacheStoragePolicy>(policy) != CFCachedURLResponseGetStoragePolicy(cachedResponse))
214         cachedResponse = CFCachedURLResponseCreateWithUserInfo(kCFAllocatorDefault, 
215                                                                CFCachedURLResponseGetWrappedResponse(cachedResponse),
216                                                                CFCachedURLResponseGetReceiverData(cachedResponse),
217                                                                CFCachedURLResponseGetUserInfo(cachedResponse), 
218                                                                static_cast<CFURLCacheStoragePolicy>(policy));
219     CFRetain(cachedResponse);
220
221     return cachedResponse;
222 }
223
224 void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
225 {
226     ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo));
227     ASSERT(handle);
228     LOG(Network, "CFNet - didReceiveChallenge(conn=%p, handle=%p (%s)", conn, handle, handle->request().url().string().utf8().data());
229
230     handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, handle));
231 }
232
233 void addHeadersFromHashMap(CFMutableURLRequestRef request, const HTTPHeaderMap& requestHeaders) 
234 {
235     if (!requestHeaders.size())
236         return;
237
238     HTTPHeaderMap::const_iterator end = requestHeaders.end();
239     for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) {
240         CFStringRef key = it->first.createCFString();
241         CFStringRef value = it->second.createCFString();
242         CFURLRequestSetHTTPHeaderFieldValue(request, key, value);
243         CFRelease(key);
244         CFRelease(value);
245     }
246 }
247
248 ResourceHandleInternal::~ResourceHandleInternal()
249 {
250     if (m_connection) {
251         LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection, m_request.url().string().utf8().data());
252         CFURLConnectionCancel(m_connection.get());
253     }
254 }
255
256 ResourceHandle::~ResourceHandle()
257 {
258     LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_request.url().string().utf8().data());
259 }
260
261 CFArrayRef arrayFromFormData(const FormData& d)
262 {
263     size_t size = d.elements().size();
264     CFMutableArrayRef a = CFArrayCreateMutable(0, d.elements().size(), &kCFTypeArrayCallBacks);
265     for (size_t i = 0; i < size; ++i) {
266         const FormDataElement& e = d.elements()[i];
267         if (e.m_type == FormDataElement::data) {
268             CFDataRef data = CFDataCreate(0, (const UInt8*)e.m_data.data(), e.m_data.size());
269             CFArrayAppendValue(a, data);
270             CFRelease(data);
271         } else {
272             ASSERT(e.m_type == FormDataElement::encodedFile);
273             CFStringRef filename = e.m_filename.createCFString();
274             CFArrayAppendValue(a, filename);
275             CFRelease(filename);
276         }
277     }
278     return a;
279 }
280
281 void emptyPerform(void* unused) 
282 {
283 }
284
285 static CFRunLoopRef loaderRL = 0;
286 void* runLoaderThread(void *unused)
287 {
288     loaderRL = CFRunLoopGetCurrent();
289
290     // Must add a source to the run loop to prevent CFRunLoopRun() from exiting
291     CFRunLoopSourceContext ctxt = {0, (void *)1 /*must be non-NULL*/, 0, 0, 0, 0, 0, 0, 0, emptyPerform};
292     CFRunLoopSourceRef bogusSource = CFRunLoopSourceCreate(0, 0, &ctxt);
293     CFRunLoopAddSource(loaderRL, bogusSource,kCFRunLoopDefaultMode);
294
295     CFRunLoopRun();
296
297     return 0;
298 }
299
300 CFRunLoopRef ResourceHandle::loaderRunLoop()
301 {
302     if (!loaderRL) {
303         createThread(runLoaderThread, 0, "WebCore: CFNetwork Loader");
304         while (loaderRL == 0) {
305             // FIXME: sleep 10? that can't be right...
306             Sleep(10);
307         }
308     }
309     return loaderRL;
310 }
311
312 static CFURLRequestRef makeFinalRequest(const ResourceRequest& request, bool shouldContentSniff)
313 {
314     CFMutableURLRequestRef newRequest = CFURLRequestCreateMutableCopy(kCFAllocatorDefault, request.cfURLRequest());
315     
316     if (!shouldContentSniff)
317         wkSetCFURLRequestShouldContentSniff(newRequest, false);
318
319     RetainPtr<CFMutableDictionaryRef> sslProps;
320
321     if (allowsAnyHTTPSCertificateHosts().contains(request.url().host().lower())) {
322         sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
323         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
324         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue);
325         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue);
326         CFDictionaryAddValue(sslProps.get(), kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
327     }
328
329     HashMap<String, RetainPtr<CFDataRef> >::iterator clientCert = clientCerts().find(request.url().host().lower());
330     if (clientCert != clientCerts().end()) {
331         if (!sslProps)
332             sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
333         wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->second).get());
334     }
335
336     if (sslProps)
337         CFURLRequestSetSSLProperties(newRequest, sslProps.get());
338
339     if (CFHTTPCookieStorageRef cookieStorage = currentCookieStorage()) {
340         CFURLRequestSetHTTPCookieStorage(newRequest, cookieStorage);
341         CFURLRequestSetHTTPCookieStorageAcceptPolicy(newRequest, CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage));
342     }
343
344     return newRequest;
345 }
346
347 bool ResourceHandle::start(Frame* frame)
348 {
349     // If we are no longer attached to a Page, this must be an attempted load from an
350     // onUnload handler, so let's just block it.
351     if (!frame->page())
352         return false;
353
354     if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !d->m_request.url().protocolInHTTPFamily()) {
355         // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made.
356         KURL urlWithCredentials(d->m_request.url());
357         urlWithCredentials.setUser(d->m_user);
358         urlWithCredentials.setPass(d->m_pass);
359         d->m_request.setURL(urlWithCredentials);
360     }
361
362     RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(d->m_request, d->m_shouldContentSniff));
363
364     CFURLConnectionClient_V3 client = { 3, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0};
365
366     d->m_connection.adoptCF(CFURLConnectionCreate(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client)));
367
368     CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection.get());
369     CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
370     CFURLConnectionStart(d->m_connection.get());
371
372     LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", d->m_request.url().string().utf8().data(), this, d->m_connection);
373
374     return true;
375 }
376
377 void ResourceHandle::cancel()
378 {
379     if (d->m_connection) {
380         CFURLConnectionCancel(d->m_connection.get());
381         d->m_connection = 0;
382     }
383 }
384
385 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
386 {
387     ASSERT_NOT_REACHED();
388     return 0;
389 }
390
391 bool ResourceHandle::supportsBufferedData()
392 {
393     return false;
394 }
395
396 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
397 {
398     const KURL& url = request.url();
399     d->m_user = url.user();
400     d->m_pass = url.pass();
401     request.removeCredentials();
402
403     client()->willSendRequest(this, request, redirectResponse);
404 }
405
406 bool ResourceHandle::shouldUseCredentialStorage()
407 {
408     LOG(Network, "CFNet - shouldUseCredentialStorage()");
409     if (client())
410         return client()->shouldUseCredentialStorage(this);
411
412     return false;
413 }
414
415 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
416 {
417     LOG(Network, "CFNet - didReceiveAuthenticationChallenge()");
418     ASSERT(!d->m_currentCFChallenge);
419     ASSERT(d->m_currentWebChallenge.isNull());
420     // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef,
421     // we make sure that is actually present
422     ASSERT(challenge.cfURLAuthChallengeRef());
423
424     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
425         RetainPtr<CFStringRef> user(AdoptCF, d->m_user.createCFString());
426         RetainPtr<CFStringRef> pass(AdoptCF, d->m_pass.createCFString());
427         RetainPtr<CFURLCredentialRef> credential(AdoptCF,
428             CFURLCredentialCreate(kCFAllocatorDefault, user.get(), pass.get(), 0, kCFURLCredentialPersistenceNone));
429         WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()), credential.get());
430         CFURLConnectionUseCredential(d->m_connection.get(), credential.get(), challenge.cfURLAuthChallengeRef());
431         d->m_user = String();
432         d->m_pass = String();
433         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
434         return;
435     }
436
437     if (!challenge.previousFailureCount() && (!client() || client()->shouldUseCredentialStorage(this))) {
438         CFURLCredentialRef credential = WebCoreCredentialStorage::get(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()));
439         if (credential) {
440             ASSERT(CFURLCredentialGetPersistence(credential) == kCFURLCredentialPersistenceNone);
441             CFURLConnectionUseCredential(d->m_connection.get(), credential, challenge.cfURLAuthChallengeRef());
442             return;
443         }
444     }
445
446     d->m_currentCFChallenge = challenge.cfURLAuthChallengeRef();
447     d->m_currentWebChallenge = AuthenticationChallenge(d->m_currentCFChallenge, this);
448     
449     if (client())
450         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
451 }
452
453 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
454 {
455     LOG(Network, "CFNet - receivedCredential()");
456     ASSERT(!challenge.isNull());
457     ASSERT(challenge.cfURLAuthChallengeRef());
458     if (challenge != d->m_currentWebChallenge)
459         return;
460
461     if (credential.persistence() == CredentialPersistenceForSession) {
462         // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way
463         // to ignore it for a particular request (short of removing it altogether).
464         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
465         RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(webCredential));
466         WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()), cfCredential.get());
467         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
468     } else {
469         RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
470         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
471     }
472
473     clearAuthentication();
474 }
475
476 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
477 {
478     LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()");
479     ASSERT(!challenge.isNull());
480     ASSERT(challenge.cfURLAuthChallengeRef());
481     if (challenge != d->m_currentWebChallenge)
482         return;
483
484     CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef());
485
486     clearAuthentication();
487 }
488
489 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
490 {
491     LOG(Network, "CFNet - receivedCancellation()");
492     if (challenge != d->m_currentWebChallenge)
493         return;
494
495     if (client())
496         client()->receivedCancellation(this, challenge);
497 }
498
499 CFURLConnectionRef ResourceHandle::connection() const
500 {
501     return d->m_connection.get();
502 }
503
504 CFURLConnectionRef ResourceHandle::releaseConnectionForDownload()
505 {
506     LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get());
507     return d->m_connection.releaseRef();
508 }
509
510 void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& vector, Frame*)
511 {
512     ASSERT(!request.isEmpty());
513
514     RetainPtr<CFDataRef> data = WebCoreSynchronousLoader::load(request, storedCredentials, response, error);
515
516     if (!error.isNull()) {
517         response = ResourceResponse(request.url(), String(), 0, String(), String());
518
519         CFErrorRef cfError = error;
520         CFStringRef domain = CFErrorGetDomain(cfError);
521         // FIXME: Return the actual response for failed authentication.
522         if (domain == kCFErrorDomainCFNetwork)
523             response.setHTTPStatusCode(CFErrorGetCode(cfError));
524         else
525             response.setHTTPStatusCode(404);
526     }
527
528     if (data) {
529         ASSERT(vector.isEmpty());
530         vector.append(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get()));
531     }
532 }
533
534 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
535 {
536     allowsAnyHTTPSCertificateHosts().add(host.lower());
537 }
538
539 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
540 {
541     clientCerts().set(host.lower(), cert);
542 }
543
544 void ResourceHandle::setDefersLoading(bool defers)
545 {
546     if (!d->m_connection)
547         return;
548
549     if (defers)
550         CFURLConnectionHalt(d->m_connection.get());
551     else
552         CFURLConnectionResume(d->m_connection.get());
553 }
554
555 bool ResourceHandle::loadsBlocked()
556 {
557     return false;
558 }
559
560 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame* frame)
561 {
562     request.setCachePolicy(ReturnCacheDataDontLoad);
563     
564     CFURLResponseRef cfResponse = 0;
565     CFErrorRef cfError = 0;
566     RetainPtr<CFURLRequestRef> cfRequest(AdoptCF, makeFinalRequest(request, true));
567     RetainPtr<CFDataRef> data(AdoptCF, CFURLConnectionSendSynchronousRequest(cfRequest.get(), &cfResponse, &cfError, request.timeoutInterval()));
568     bool cached = cfResponse && !cfError;
569
570     if (cfError)
571         CFRelease(cfError);
572     if (cfResponse)
573         CFRelease(cfResponse);
574
575     return cached;
576 }
577
578 CFURLRequestRef WebCoreSynchronousLoader::willSendRequest(CFURLConnectionRef, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo)
579 {
580     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
581
582     // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
583     if (loader->m_url && !protocolHostAndPortAreEqual(loader->m_url.get(), CFURLRequestGetURL(cfRequest))) {
584         RetainPtr<CFErrorRef> cfError(AdoptCF, CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCFNetwork, kCFURLErrorBadServerResponse, 0));
585         loader->m_error = cfError.get();
586         loader->m_isDone = true;
587         return 0;
588     }
589
590     loader->m_url = CFURLRequestGetURL(cfRequest);
591
592     if (cfRedirectResponse) {
593         // Take user/pass out of the URL.
594         loader->m_user.adoptCF(CFURLCopyUserName(loader->m_url.get()));
595         loader->m_pass.adoptCF(CFURLCopyPassword(loader->m_url.get()));
596         if (loader->m_user || loader->m_pass) {
597             ResourceRequest requestWithoutCredentials = cfRequest;
598             requestWithoutCredentials.removeCredentials();
599             cfRequest = requestWithoutCredentials.cfURLRequest();
600         }
601     }
602
603     CFRetain(cfRequest);
604     return cfRequest;
605 }
606
607 void WebCoreSynchronousLoader::didReceiveResponse(CFURLConnectionRef, CFURLResponseRef cfResponse, const void* clientInfo) 
608 {
609     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
610
611     loader->m_response = cfResponse;
612 }
613
614 void WebCoreSynchronousLoader::didReceiveData(CFURLConnectionRef, CFDataRef data, CFIndex originalLength, const void* clientInfo)
615 {
616     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
617
618     if (!loader->m_data)
619         loader->m_data.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
620
621     const UInt8* bytes = CFDataGetBytePtr(data);
622     CFIndex length = CFDataGetLength(data);
623
624     CFDataAppendBytes(loader->m_data.get(), bytes, length);
625 }
626
627 void WebCoreSynchronousLoader::didFinishLoading(CFURLConnectionRef, const void* clientInfo)
628 {
629     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
630
631     loader->m_isDone = true;
632 }
633
634 void WebCoreSynchronousLoader::didFail(CFURLConnectionRef, CFErrorRef error, const void* clientInfo)
635 {
636     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
637
638     loader->m_error = error;
639     loader->m_isDone = true;
640 }
641
642 void WebCoreSynchronousLoader::didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo)
643 {
644     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
645
646     if (loader->m_user && loader->m_pass) {
647         RetainPtr<CFURLCredentialRef> credential(AdoptCF,
648             CFURLCredentialCreate(kCFAllocatorDefault, loader->m_user.get(), loader->m_pass.get(), 0, kCFURLCredentialPersistenceNone));
649         WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge), credential.get());
650         CFURLConnectionUseCredential(conn, credential.get(), challenge);
651         loader->m_user = 0;
652         loader->m_pass = 0;
653         return;
654     }
655     if (!CFURLAuthChallengeGetPreviousFailureCount(challenge) && loader->m_allowStoredCredentials) {
656         CFURLCredentialRef credential = WebCoreCredentialStorage::get(CFURLAuthChallengeGetProtectionSpace(challenge));
657         if (credential) {
658             ASSERT(CFURLCredentialGetPersistence(credential) == kCFURLCredentialPersistenceNone);
659             CFURLConnectionUseCredential(conn, credential, challenge);
660             return;
661         }
662     }
663     // FIXME: The user should be asked for credentials, as in async case.
664     CFURLConnectionUseCredential(conn, 0, challenge);
665 }
666
667 Boolean WebCoreSynchronousLoader::shouldUseCredentialStorage(CFURLConnectionRef, const void* clientInfo)
668 {
669     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
670
671     // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
672     return loader->m_allowStoredCredentials;
673 }
674
675 RetainPtr<CFDataRef> WebCoreSynchronousLoader::load(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceResponse& response, ResourceError& error)
676 {
677     ASSERT(response.isNull());
678     ASSERT(error.isNull());
679
680     WebCoreSynchronousLoader loader(response, error);
681
682     KURL url = request.url();
683
684     loader.m_user.adoptCF(url.user().createCFString());
685     loader.m_pass.adoptCF(url.pass().createCFString());
686     loader.m_allowStoredCredentials = (storedCredentials == AllowStoredCredentials);
687
688     // Take user/pass out of the URL.
689     // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made.
690     RetainPtr<CFURLRequestRef> cfRequest;
691     if ((loader.m_user || loader.m_pass) && url.protocolInHTTPFamily()) {
692         ResourceRequest requestWithoutCredentials(request);
693         requestWithoutCredentials.removeCredentials();
694         cfRequest.adoptCF(makeFinalRequest(requestWithoutCredentials, ResourceHandle::shouldContentSniffURL(requestWithoutCredentials.url())));
695     } else
696         cfRequest.adoptCF(makeFinalRequest(request, ResourceHandle::shouldContentSniffURL(request.url())));
697
698     CFURLConnectionClient_V3 client = { 3, &loader, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, 0, didReceiveChallenge, 0, shouldUseCredentialStorage, 0 };
699     RetainPtr<CFURLConnectionRef> connection(AdoptCF, CFURLConnectionCreate(kCFAllocatorDefault, cfRequest.get(), reinterpret_cast<CFURLConnectionClient*>(&client)));
700
701     CFURLConnectionScheduleWithRunLoop(connection.get(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode);
702     CFURLConnectionScheduleDownloadWithRunLoop(connection.get(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode);
703     CFURLConnectionStart(connection.get());
704
705     while (!loader.m_isDone)
706         CFRunLoopRunInMode(WebCoreSynchronousLoaderRunLoopMode, UINT_MAX, true);
707
708     CFURLConnectionCancel(connection.get());
709     
710     if (error.isNull() && loader.m_response.mimeType().isNull())
711         setDefaultMIMEType(loader.m_response.cfURLResponse());
712
713     return loader.m_data;
714 }
715
716 } // namespace WebCore