1/* 2 * Copyright (C) 2004, 2006 Apple Inc. All rights reserved. 3 * Copyright (C) 2005, 2006 Michael Emmel mike.emmel@gmail.com 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "config.h" 29#include "ResourceHandle.h" 30 31#if USE(CURL) 32 33#include "CachedResourceLoader.h" 34#include "CredentialStorage.h" 35#include "FileSystem.h" 36#include "Logging.h" 37#include "NetworkingContext.h" 38#include "NotImplemented.h" 39#include "ResourceHandleInternal.h" 40#include "ResourceHandleManager.h" 41#include "SSLHandle.h" 42 43#if PLATFORM(WIN) && USE(CF) 44#include <wtf/PassRefPtr.h> 45#include <wtf/RetainPtr.h> 46#endif 47 48namespace WebCore { 49 50class WebCoreSynchronousLoader : public ResourceHandleClient { 51public: 52 WebCoreSynchronousLoader(); 53 54 virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); 55 virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int encodedDataLength); 56 virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); 57 virtual void didFail(ResourceHandle*, const ResourceError&); 58 59 ResourceResponse resourceResponse() const { return m_response; } 60 ResourceError resourceError() const { return m_error; } 61 Vector<char> data() const { return m_data; } 62 63private: 64 ResourceResponse m_response; 65 ResourceError m_error; 66 Vector<char> m_data; 67}; 68 69WebCoreSynchronousLoader::WebCoreSynchronousLoader() 70{ 71} 72 73void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) 74{ 75 m_response = response; 76} 77 78void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, unsigned length, int) 79{ 80 m_data.append(data, length); 81} 82 83void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double) 84{ 85} 86 87void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) 88{ 89 m_error = error; 90} 91 92ResourceHandleInternal::~ResourceHandleInternal() 93{ 94 fastFree(m_url); 95 if (m_customHeaders) 96 curl_slist_free_all(m_customHeaders); 97} 98 99ResourceHandle::~ResourceHandle() 100{ 101 cancel(); 102} 103 104bool ResourceHandle::start() 105{ 106 // The frame could be null if the ResourceHandle is not associated to any 107 // Frame, e.g. if we are downloading a file. 108 // If the frame is not null but the page is null this must be an attempted 109 // load from an unload handler, so let's just block it. 110 // If both the frame and the page are not null the context is valid. 111 if (d->m_context && !d->m_context->isValid()) 112 return false; 113 114 ResourceHandleManager::sharedInstance()->add(this); 115 return true; 116} 117 118void ResourceHandle::cancel() 119{ 120 ResourceHandleManager::sharedInstance()->cancel(this); 121} 122 123void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host) 124{ 125 allowsAnyHTTPSCertificateHosts(host.lower()); 126} 127 128void ResourceHandle::setClientCertificateInfo(const String& host, const String& certificate, const String& key) 129{ 130 if (fileExists(certificate)) 131 addAllowedClientCertificate(host, certificate, key); 132 else 133 LOG(Network, "Invalid client certificate file: %s!\n", certificate.latin1().data()); 134} 135 136#if PLATFORM(WIN) && USE(CF) 137// FIXME: The CFDataRef will need to be something else when 138// building without 139static HashMap<String, RetainPtr<CFDataRef> >& clientCerts() 140{ 141 static HashMap<String, RetainPtr<CFDataRef> > certs; 142 return certs; 143} 144 145void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert) 146{ 147 clientCerts().set(host.lower(), cert); 148} 149#endif 150 151void ResourceHandle::platformSetDefersLoading(bool defers) 152{ 153 if (!d->m_handle) 154 return; 155 156 if (defers) { 157 CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_ALL); 158 // If we could not defer the handle, so don't do it. 159 if (error != CURLE_OK) 160 return; 161 } else { 162 CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_CONT); 163 if (error != CURLE_OK) 164 // Restarting the handle has failed so just cancel it. 165 cancel(); 166 } 167} 168 169bool ResourceHandle::loadsBlocked() 170{ 171 notImplemented(); 172 return false; 173} 174 175bool ResourceHandle::shouldUseCredentialStorage() 176{ 177 return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily(); 178} 179 180void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) 181{ 182 WebCoreSynchronousLoader syncLoader; 183 RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &syncLoader, true, false)); 184 185 ResourceHandleManager* manager = ResourceHandleManager::sharedInstance(); 186 187 manager->dispatchSynchronousJob(handle.get()); 188 189 error = syncLoader.resourceError(); 190 data = syncLoader.data(); 191 response = syncLoader.resourceResponse(); 192} 193 194void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) 195{ 196 if (!d->m_user.isNull() && !d->m_pass.isNull()) { 197 Credential credential(d->m_user, d->m_pass, CredentialPersistenceNone); 198 199 URL urlToStore; 200 if (challenge.failureResponse().httpStatusCode() == 401) 201 urlToStore = challenge.failureResponse().url(); 202 CredentialStorage::set(credential, challenge.protectionSpace(), urlToStore); 203 204 String userpass = credential.user() + ":" + credential.password(); 205 curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data()); 206 207 d->m_user = String(); 208 d->m_pass = String(); 209 // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly. 210 return; 211 } 212 213 if (shouldUseCredentialStorage()) { 214 if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) { 215 // The stored credential wasn't accepted, stop using it. 216 // There is a race condition here, since a different credential might have already been stored by another ResourceHandle, 217 // but the observable effect should be very minor, if any. 218 CredentialStorage::remove(challenge.protectionSpace()); 219 } 220 221 if (!challenge.previousFailureCount()) { 222 Credential credential = CredentialStorage::get(challenge.protectionSpace()); 223 if (!credential.isEmpty() && credential != d->m_initialCredential) { 224 ASSERT(credential.persistence() == CredentialPersistenceNone); 225 if (challenge.failureResponse().httpStatusCode() == 401) { 226 // Store the credential back, possibly adding it as a default for this directory. 227 CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url()); 228 } 229 String userpass = credential.user() + ":" + credential.password(); 230 curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data()); 231 return; 232 } 233 } 234 } 235 236 d->m_currentWebChallenge = challenge; 237 238 if (client()) 239 client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge); 240} 241 242void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential) 243{ 244 if (challenge != d->m_currentWebChallenge) 245 return; 246 247 if (credential.isEmpty()) { 248 receivedRequestToContinueWithoutCredential(challenge); 249 return; 250 } 251 252 if (shouldUseCredentialStorage()) { 253 if (challenge.failureResponse().httpStatusCode() == 401) { 254 URL urlToStore = challenge.failureResponse().url(); 255 CredentialStorage::set(credential, challenge.protectionSpace(), urlToStore); 256 } 257 } 258 259 String userpass = credential.user() + ":" + credential.password(); 260 curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data()); 261 262 clearAuthentication(); 263} 264 265void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge) 266{ 267 if (challenge != d->m_currentWebChallenge) 268 return; 269 270 String userpass = ""; 271 curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data()); 272 273 clearAuthentication(); 274} 275 276void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge) 277{ 278 if (challenge != d->m_currentWebChallenge) 279 return; 280 281 if (client()) 282 client()->receivedCancellation(this, challenge); 283} 284 285void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) 286{ 287 ASSERT_NOT_REACHED(); 288} 289 290void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge&) 291{ 292 ASSERT_NOT_REACHED(); 293} 294 295} // namespace WebCore 296 297#endif 298