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