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