[Curl] Basic authentication is not reused.
[WebKit-https.git] / Source / WebCore / platform / network / curl / ResourceHandleCurl.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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 #include "CachedResourceLoader.h"
32 #include "CredentialStorage.h"
33 #include "NetworkingContext.h"
34 #include "NotImplemented.h"
35 #include "ResourceHandleInternal.h"
36 #include "ResourceHandleManager.h"
37
38 #if PLATFORM(WIN) && USE(CF)
39 #include <wtf/PassRefPtr.h>
40 #include <wtf/RetainPtr.h>
41 #endif
42
43 namespace WebCore {
44
45 class WebCoreSynchronousLoader : public ResourceHandleClient {
46 public:
47     WebCoreSynchronousLoader();
48
49     virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
50     virtual void didReceiveData(ResourceHandle*, const char*, int, int encodedDataLength);
51     virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
52     virtual void didFail(ResourceHandle*, const ResourceError&);
53
54     ResourceResponse resourceResponse() const { return m_response; }
55     ResourceError resourceError() const { return m_error; }
56     Vector<char> data() const { return m_data; }
57
58 private:
59     ResourceResponse m_response;
60     ResourceError m_error;
61     Vector<char> m_data;
62 };
63
64 WebCoreSynchronousLoader::WebCoreSynchronousLoader()
65 {
66 }
67
68 void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
69 {
70     m_response = response;
71 }
72
73 void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int)
74 {
75     m_data.append(data, length);
76 }
77
78 void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double)
79 {
80 }
81
82 void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
83 {
84     m_error = error;
85 }
86
87 ResourceHandleInternal::~ResourceHandleInternal()
88 {
89     fastFree(m_url);
90     if (m_customHeaders)
91         curl_slist_free_all(m_customHeaders);
92 }
93
94 ResourceHandle::~ResourceHandle()
95 {
96     cancel();
97 }
98
99 bool ResourceHandle::start()
100 {
101     // The frame could be null if the ResourceHandle is not associated to any
102     // Frame, e.g. if we are downloading a file.
103     // If the frame is not null but the page is null this must be an attempted
104     // load from an unload handler, so let's just block it.
105     // If both the frame and the page are not null the context is valid.
106     if (d->m_context && !d->m_context->isValid())
107         return false;
108
109     ResourceHandleManager::sharedInstance()->add(this);
110     return true;
111 }
112
113 void ResourceHandle::cancel()
114 {
115     ResourceHandleManager::sharedInstance()->cancel(this);
116 }
117
118 #if PLATFORM(WIN) && USE(CF)
119 static HashSet<String>& allowsAnyHTTPSCertificateHosts()
120 {
121     static HashSet<String> hosts;
122
123     return hosts;
124 }
125
126 void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host)
127 {
128     allowsAnyHTTPSCertificateHosts().add(host.lower());
129 }
130 #endif
131
132 #if PLATFORM(WIN) && USE(CF)
133 // FIXME:  The CFDataRef will need to be something else when
134 // building without 
135 static HashMap<String, RetainPtr<CFDataRef> >& clientCerts()
136 {
137     static HashMap<String, RetainPtr<CFDataRef> > certs;
138     return certs;
139 }
140
141 void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert)
142 {
143     clientCerts().set(host.lower(), cert);
144 }
145 #endif
146
147 void ResourceHandle::platformSetDefersLoading(bool defers)
148 {
149     if (!d->m_handle)
150         return;
151
152     if (defers) {
153         CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_ALL);
154         // If we could not defer the handle, so don't do it.
155         if (error != CURLE_OK)
156             return;
157     } else {
158         CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_CONT);
159         if (error != CURLE_OK)
160             // Restarting the handle has failed so just cancel it.
161             cancel();
162     }
163 }
164
165 bool ResourceHandle::loadsBlocked()
166 {
167     notImplemented();
168     return false;
169 }
170
171 bool ResourceHandle::shouldUseCredentialStorage()
172 {
173     return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily();
174 }
175
176 void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
177 {
178     WebCoreSynchronousLoader syncLoader;
179     RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(context, request, &syncLoader, true, false));
180
181     ResourceHandleManager* manager = ResourceHandleManager::sharedInstance();
182
183     manager->dispatchSynchronousJob(handle.get());
184
185     error = syncLoader.resourceError();
186     data = syncLoader.data();
187     response = syncLoader.resourceResponse();
188 }
189
190 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
191 {
192     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
193         Credential credential(d->m_user, d->m_pass, CredentialPersistenceNone);
194
195         URL urlToStore;
196         if (challenge.failureResponse().httpStatusCode() == 401)
197             urlToStore = challenge.failureResponse().url();
198         CredentialStorage::set(credential, challenge.protectionSpace(), urlToStore);
199         
200         String userpass = credential.user() + ":" + credential.password();
201         curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data());
202
203         d->m_user = String();
204         d->m_pass = String();
205         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
206         return;
207     }
208
209     if (shouldUseCredentialStorage()) {
210         if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
211             // The stored credential wasn't accepted, stop using it.
212             // There is a race condition here, since a different credential might have already been stored by another ResourceHandle,
213             // but the observable effect should be very minor, if any.
214             CredentialStorage::remove(challenge.protectionSpace());
215         }
216
217         if (!challenge.previousFailureCount()) {
218             Credential credential = CredentialStorage::get(challenge.protectionSpace());
219             if (!credential.isEmpty() && credential != d->m_initialCredential) {
220                 ASSERT(credential.persistence() == CredentialPersistenceNone);
221                 if (challenge.failureResponse().httpStatusCode() == 401) {
222                     // Store the credential back, possibly adding it as a default for this directory.
223                     CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
224                 }
225                 String userpass = credential.user() + ":" + credential.password();
226                 curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data());
227                 return;
228             }
229         }
230     }
231
232     d->m_currentWebChallenge = challenge;
233     
234     if (client())
235         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
236 }
237
238 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
239 {
240     if (challenge != d->m_currentWebChallenge)
241         return;
242
243     if (credential.isEmpty()) {
244         receivedRequestToContinueWithoutCredential(challenge);
245         return;
246     }
247
248     if (shouldUseCredentialStorage()) {
249         if (challenge.failureResponse().httpStatusCode() == 401) {
250             URL urlToStore = challenge.failureResponse().url();
251             CredentialStorage::set(credential, challenge.protectionSpace(), urlToStore);
252         }
253     }
254
255     String userpass = credential.user() + ":" + credential.password();
256     curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data());
257
258     clearAuthentication();
259 }
260
261 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
262 {
263     if (challenge != d->m_currentWebChallenge)
264         return;
265
266     String userpass = "";
267     curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data());
268
269     clearAuthentication();
270 }
271
272 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
273 {
274     if (challenge != d->m_currentWebChallenge)
275         return;
276
277     if (client())
278         client()->receivedCancellation(this, challenge);
279 }
280
281 } // namespace WebCore