Safari 4 cannot be used to update firmware on Linksys routers.
[WebKit-https.git] / WebCore / platform / network / mac / ResourceHandleMac.mm
1 /*
2  * Copyright (C) 2004, 2006-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 #import "config.h"
27 #import "ResourceHandleInternal.h"
28
29 #import "AuthenticationChallenge.h"
30 #import "AuthenticationMac.h"
31 #import "BlockExceptions.h"
32 #import "CString.h"
33 #import "CredentialStorage.h"
34 #import "DocLoader.h"
35 #import "FormDataStreamMac.h"
36 #import "Frame.h"
37 #import "FrameLoader.h"
38 #import "Logging.h"
39 #import "MIMETypeRegistry.h"
40 #import "Page.h"
41 #import "ResourceError.h"
42 #import "ResourceResponse.h"
43 #import "SchedulePair.h"
44 #import "Settings.h"
45 #import "SharedBuffer.h"
46 #import "SubresourceLoader.h"
47 #import "WebCoreSystemInterface.h"
48 #import "WebCoreURLResponse.h"
49 #import <wtf/UnusedParam.h>
50
51 #ifdef BUILDING_ON_TIGER
52 typedef int NSInteger;
53 #endif
54
55 using namespace WebCore;
56
57 @interface WebCoreResourceHandleAsDelegate : NSObject <NSURLAuthenticationChallengeSender>
58 {
59     ResourceHandle* m_handle;
60 }
61 - (id)initWithHandle:(ResourceHandle*)handle;
62 - (void)detachHandle;
63 @end
64
65 @interface NSURLConnection (NSURLConnectionTigerPrivate)
66 - (NSData *)_bufferedData;
67 @end
68
69 @interface NSURLRequest (Details)
70 - (id)_propertyForKey:(NSString *)key;
71 @end
72
73 #ifndef BUILDING_ON_TIGER
74
75 @interface WebCoreSynchronousLoader : NSObject {
76     NSURL *m_url;
77     NSString *m_user;
78     NSString *m_pass;
79     // Store the preemptively used initial credential so that if we get an authentication challenge, we won't use the same one again.
80     Credential m_initialCredential;
81     BOOL m_allowStoredCredentials;
82     NSURLResponse *m_response;
83     NSMutableData *m_data;
84     NSError *m_error;
85     BOOL m_isDone;
86 }
87 + (NSData *)loadRequest:(NSURLRequest *)request allowStoredCredentials:(BOOL)allowStoredCredentials returningResponse:(NSURLResponse **)response error:(NSError **)error;
88 @end
89
90 static NSString *WebCoreSynchronousLoaderRunLoopMode = @"WebCoreSynchronousLoaderRunLoopMode";
91
92 #endif
93
94 namespace WebCore {
95
96 #ifdef BUILDING_ON_TIGER
97 static unsigned inNSURLConnectionCallback;
98 #endif
99
100 #ifndef NDEBUG
101 static bool isInitializingConnection;
102 #endif
103     
104 class CallbackGuard {
105 public:
106     CallbackGuard()
107     {
108 #ifdef BUILDING_ON_TIGER
109         ++inNSURLConnectionCallback;
110 #endif
111     }
112     ~CallbackGuard()
113     {
114 #ifdef BUILDING_ON_TIGER
115         ASSERT(inNSURLConnectionCallback > 0);
116         --inNSURLConnectionCallback;
117 #endif
118     }
119 };
120
121 ResourceHandleInternal::~ResourceHandleInternal()
122 {
123 }
124
125 ResourceHandle::~ResourceHandle()
126 {
127     releaseDelegate();
128
129     LOG(Network, "Handle %p destroyed", this);
130 }
131
132 static const double MaxFoundationVersionWithoutdidSendBodyDataDelegate = 677.21;
133 bool ResourceHandle::didSendBodyDataDelegateExists()
134 {
135     return NSFoundationVersionNumber > MaxFoundationVersionWithoutdidSendBodyDataDelegate;
136 }
137
138 bool ResourceHandle::start(Frame* frame)
139 {
140     if (!frame)
141         return false;
142
143     BEGIN_BLOCK_OBJC_EXCEPTIONS;
144
145     // If we are no longer attached to a Page, this must be an attempted load from an
146     // onUnload handler, so let's just block it.
147     Page* page = frame->page();
148     if (!page)
149         return false;
150
151 #ifndef NDEBUG
152     isInitializingConnection = YES;
153 #endif
154
155     id delegate;
156     
157     if (d->m_mightDownloadFromHandle) {
158         ASSERT(!d->m_proxy);
159         d->m_proxy = wkCreateNSURLConnectionDelegateProxy();
160         [d->m_proxy.get() setDelegate:ResourceHandle::delegate()];
161         [d->m_proxy.get() release];
162         
163         delegate = d->m_proxy.get();
164     } else 
165         delegate = ResourceHandle::delegate();
166
167     if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !d->m_request.url().protocolInHTTPFamily()) {
168         // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
169         KURL urlWithCredentials(d->m_request.url());
170         urlWithCredentials.setUser(d->m_user);
171         urlWithCredentials.setPass(d->m_pass);
172         d->m_request.setURL(urlWithCredentials);
173     }
174     
175     // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
176     // try and reuse the credential preemptively, as allowed by RFC 2617.
177     if (!client() || client()->shouldUseCredentialStorage(this) && d->m_request.url().protocolInHTTPFamily())
178         d->m_initialCredential = CredentialStorage::getDefaultAuthenticationCredential(d->m_request.url());
179         
180     if (!d->m_initialCredential.isEmpty()) {
181         // Apply basic auth header
182         String unencoded = d->m_initialCredential.user() + ":" + d->m_initialCredential.password();
183         CString encoded = unencoded.utf8().base64Encode();
184         String authHeader = String::format("Basic %s", encoded.data());
185         d->m_request.addHTTPHeaderField("Authorization", authHeader);
186     }
187
188     if (!ResourceHandle::didSendBodyDataDelegateExists())
189         associateStreamWithResourceHandle([d->m_request.nsURLRequest() HTTPBodyStream], this);
190
191 #ifdef BUILDING_ON_TIGER
192     // A conditional request sent by WebCore (e.g. to update appcache) can be for a resource that is not cacheable by NSURLConnection,
193     // which can get confused and fail to load it in this case.
194     if (d->m_request.isConditional())
195         d->m_request.setCachePolicy(ReloadIgnoringCacheData);
196 #endif
197
198     d->m_needsSiteSpecificQuirks = frame->settings() && frame->settings()->needsSiteSpecificQuirks();
199
200     NSURLConnection *connection;
201     
202     if (d->m_shouldContentSniff || frame->settings()->localFileContentSniffingEnabled()) 
203 #ifdef BUILDING_ON_TIGER
204         connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate];
205 #else
206         connection = [[NSURLConnection alloc] initWithRequest:d->m_request.nsURLRequest() delegate:delegate startImmediately:NO];
207 #endif
208     else {
209         NSMutableURLRequest *request = [d->m_request.nsURLRequest() mutableCopy];
210         wkSetNSURLRequestShouldContentSniff(request, NO);
211 #ifdef BUILDING_ON_TIGER
212         connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
213 #else
214         connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
215 #endif
216         [request release];
217     }
218
219 #ifndef BUILDING_ON_TIGER
220     bool scheduled = false;
221     if (SchedulePairHashSet* scheduledPairs = page->scheduledRunLoopPairs()) {
222         SchedulePairHashSet::iterator end = scheduledPairs->end();
223         for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) {
224             if (NSRunLoop *runLoop = (*it)->nsRunLoop()) {
225                 [connection scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()];
226                 scheduled = true;
227             }
228         }
229     }
230
231     // Start the connection if we did schedule with at least one runloop.
232     // We can't start the connection until we have one runloop scheduled.
233     if (scheduled)
234         [connection start];
235     else
236         d->m_startWhenScheduled = true;
237 #endif
238
239 #ifndef NDEBUG
240     isInitializingConnection = NO;
241 #endif
242
243     LOG(Network, "Handle %p starting connection %p for %@", this, connection, d->m_request.nsURLRequest());
244     
245     d->m_connection = connection;
246
247     if (d->m_connection) {
248         [connection release];
249
250         if (d->m_defersLoading)
251             wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), YES);
252
253         return true;
254     }
255
256     END_BLOCK_OBJC_EXCEPTIONS;
257
258     return false;
259 }
260
261 void ResourceHandle::cancel()
262 {
263     LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get());
264
265     // Leaks were seen on HTTP tests without this; can be removed once <rdar://problem/6886937> is fixed.
266     if (d->m_currentMacChallenge)
267         [[d->m_currentMacChallenge sender] cancelAuthenticationChallenge:d->m_currentMacChallenge];
268
269     if (!ResourceHandle::didSendBodyDataDelegateExists())
270         disassociateStreamWithResourceHandle([d->m_request.nsURLRequest() HTTPBodyStream]);
271     [d->m_connection.get() cancel];
272 }
273
274 void ResourceHandle::setDefersLoading(bool defers)
275 {
276     LOG(Network, "Handle %p setDefersLoading(%s)", this, defers ? "true" : "false");
277
278     d->m_defersLoading = defers;
279     if (d->m_connection)
280         wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers);
281 }
282
283 void ResourceHandle::schedule(SchedulePair* pair)
284 {
285 #ifndef BUILDING_ON_TIGER
286     NSRunLoop *runLoop = pair->nsRunLoop();
287     if (!runLoop)
288         return;
289     [d->m_connection.get() scheduleInRunLoop:runLoop forMode:(NSString *)pair->mode()];
290     if (d->m_startWhenScheduled) {
291         [d->m_connection.get() start];
292         d->m_startWhenScheduled = false;
293     }
294 #else
295     UNUSED_PARAM(pair);
296 #endif
297 }
298
299 void ResourceHandle::unschedule(SchedulePair* pair)
300 {
301 #ifndef BUILDING_ON_TIGER
302     if (NSRunLoop *runLoop = pair->nsRunLoop())
303         [d->m_connection.get() unscheduleFromRunLoop:runLoop forMode:(NSString *)pair->mode()];
304 #else
305     UNUSED_PARAM(pair);
306 #endif
307 }
308
309 WebCoreResourceHandleAsDelegate *ResourceHandle::delegate()
310 {
311     if (!d->m_delegate) {
312         WebCoreResourceHandleAsDelegate *delegate = [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this];
313         d->m_delegate = delegate;
314         [delegate release];
315     }
316     return d->m_delegate.get();
317 }
318
319 void ResourceHandle::releaseDelegate()
320 {
321     if (!d->m_delegate)
322         return;
323     if (d->m_proxy)
324         [d->m_proxy.get() setDelegate:nil];
325     [d->m_delegate.get() detachHandle];
326     d->m_delegate = nil;
327 }
328
329 bool ResourceHandle::supportsBufferedData()
330 {
331     static bool supportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)];
332     return supportsBufferedData;
333 }
334
335 PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
336 {
337     if (ResourceHandle::supportsBufferedData())
338         return SharedBuffer::wrapNSData([d->m_connection.get() _bufferedData]);
339
340     return 0;
341 }
342
343 id ResourceHandle::releaseProxy()
344 {
345     id proxy = [[d->m_proxy.get() retain] autorelease];
346     d->m_proxy = nil;
347     [proxy setDelegate:nil];
348     return proxy;
349 }
350
351 NSURLConnection *ResourceHandle::connection() const
352 {
353     return d->m_connection.get();
354 }
355
356 bool ResourceHandle::loadsBlocked()
357 {
358 #ifndef BUILDING_ON_TIGER
359     return false;
360 #else
361     // On Tiger, if we're in an NSURLConnection callback, that blocks all other NSURLConnection callbacks.
362     // On Leopard and newer, it blocks only callbacks on that same NSURLConnection object, which is not
363     // a problem in practice.
364     return inNSURLConnectionCallback != 0;
365 #endif
366 }
367
368 bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
369 {
370 #ifndef BUILDING_ON_TIGER
371     request.setCachePolicy(ReturnCacheDataDontLoad);
372     NSURLResponse *nsURLResponse = nil;
373     BEGIN_BLOCK_OBJC_EXCEPTIONS;
374     
375    [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil];
376     
377     END_BLOCK_OBJC_EXCEPTIONS;
378     
379     return nsURLResponse;
380 #else
381     // <rdar://problem/6803217> - Re-enable after <rdar://problem/6786454> is resolved.
382     UNUSED_PARAM(request);
383     return false;
384 #endif
385 }
386
387 void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data, Frame*)
388 {
389     NSError *nsError = nil;
390     
391     NSURLResponse *nsURLResponse = nil;
392     NSData *result = nil;
393
394     ASSERT(!request.isEmpty());
395     
396     NSURLRequest *nsRequest;
397     if (!shouldContentSniffURL(request.url())) {
398         NSMutableURLRequest *mutableRequest = [[request.nsURLRequest() mutableCopy] autorelease];
399         wkSetNSURLRequestShouldContentSniff(mutableRequest, NO);
400         nsRequest = mutableRequest;
401     } else
402         nsRequest = request.nsURLRequest();
403             
404     BEGIN_BLOCK_OBJC_EXCEPTIONS;
405     
406 #ifndef BUILDING_ON_TIGER
407     result = [WebCoreSynchronousLoader loadRequest:nsRequest allowStoredCredentials:(storedCredentials == AllowStoredCredentials) returningResponse:&nsURLResponse error:&nsError];
408 #else
409     UNUSED_PARAM(storedCredentials);
410     result = [NSURLConnection sendSynchronousRequest:nsRequest returningResponse:&nsURLResponse error:&nsError];
411 #endif
412     END_BLOCK_OBJC_EXCEPTIONS;
413
414     if (nsError == nil)
415         response = nsURLResponse;
416     else {
417         response = ResourceResponse(request.url(), String(), 0, String(), String());
418         if ([nsError domain] == NSURLErrorDomain)
419             switch ([nsError code]) {
420                 case NSURLErrorUserCancelledAuthentication:
421                     // FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one.
422                     response.setHTTPStatusCode(401);
423                     break;
424                 default:
425                     response.setHTTPStatusCode([nsError code]);
426             }
427         else
428             response.setHTTPStatusCode(404);
429     }
430     
431     data.resize([result length]);
432     memcpy(data.data(), [result bytes], [result length]);
433     
434     error = nsError;
435 }
436
437 void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
438 {
439     const KURL& url = request.url();
440     d->m_user = url.user();
441     d->m_pass = url.pass();
442     request.removeCredentials();
443
444     client()->willSendRequest(this, request, redirectResponse);
445 }
446
447 bool ResourceHandle::shouldUseCredentialStorage()
448 {
449     if (client())
450         return client()->shouldUseCredentialStorage(this);
451
452     return false;
453 }
454
455 void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
456 {
457     ASSERT(!d->m_currentMacChallenge);
458     ASSERT(d->m_currentWebChallenge.isNull());
459     // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge,
460     // we make sure that is actually present
461     ASSERT(challenge.nsURLAuthenticationChallenge());
462
463     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
464         NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user
465                                                                    password:d->m_pass
466                                                                 persistence:NSURLCredentialPersistenceNone];
467         d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
468         d->m_currentWebChallenge = challenge;
469         receivedCredential(challenge, core(credential));
470         [credential release];
471         // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
472         d->m_user = String();
473         d->m_pass = String();
474         return;
475     }
476
477     if (!challenge.previousFailureCount() && (!client() || client()->shouldUseCredentialStorage(this))) {
478         Credential credential = CredentialStorage::get(challenge.protectionSpace());
479         if (!credential.isEmpty() && credential != d->m_initialCredential) {
480             [challenge.sender() useCredential:mac(credential) forAuthenticationChallenge:mac(challenge)];
481             return;
482         }
483     }
484
485     d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
486     NSURLAuthenticationChallenge *webChallenge = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:d->m_currentMacChallenge 
487                                                                                        sender:(id<NSURLAuthenticationChallengeSender>)delegate()];
488     d->m_currentWebChallenge = core(webChallenge);
489     [webChallenge release];
490
491     if (client())
492         client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge);
493 }
494
495 void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge)
496 {
497     ASSERT(d->m_currentMacChallenge);
498     ASSERT(!d->m_currentWebChallenge.isNull());
499     ASSERT(d->m_currentWebChallenge == challenge);
500
501     if (client())
502         client()->didCancelAuthenticationChallenge(this, challenge);
503 }
504
505 void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential)
506 {
507     ASSERT(!challenge.isNull());
508     if (challenge != d->m_currentWebChallenge)
509         return;
510
511 #ifdef BUILDING_ON_TIGER
512     if (credential.persistence() == CredentialPersistenceNone) {
513         // NSURLCredentialPersistenceNone doesn't work on Tiger, so we have to use session persistence.
514         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceForSession);
515         KURL urlToStore;
516         if (challenge.failureResponse().httpStatusCode() == 401)
517             urlToStore = d->m_request.url();
518         CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore);
519         
520         [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
521     } else
522 #else
523     if (credential.persistence() == CredentialPersistenceForSession && (!d->m_needsSiteSpecificQuirks || ![[[mac(challenge) protectionSpace] host] isEqualToString:@"gallery.me.com"])) {
524         // Manage per-session credentials internally, because once NSURLCredentialPersistenceForSession is used, there is no way
525         // to ignore it for a particular request (short of removing it altogether).
526         // <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials.
527         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
528         KURL urlToStore;
529         if (challenge.failureResponse().httpStatusCode() == 401)
530             urlToStore = d->m_request.url();
531         CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore);
532         [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
533     } else
534 #endif
535         [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge];
536
537     clearAuthentication();
538 }
539
540 void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
541 {
542     ASSERT(!challenge.isNull());
543     if (challenge != d->m_currentWebChallenge)
544         return;
545
546     [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge];
547
548     clearAuthentication();
549 }
550
551 void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge)
552 {
553     if (challenge != d->m_currentWebChallenge)
554         return;
555
556     if (client())
557         client()->receivedCancellation(this, challenge);
558 }
559
560 } // namespace WebCore
561
562 @implementation WebCoreResourceHandleAsDelegate
563
564 - (id)initWithHandle:(ResourceHandle*)handle
565 {
566     self = [self init];
567     if (!self)
568         return nil;
569     m_handle = handle;
570     return self;
571 }
572
573 - (void)detachHandle
574 {
575     m_handle = 0;
576 }
577
578 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
579 {
580     UNUSED_PARAM(connection);
581
582     // the willSendRequest call may cancel this load, in which case self could be deallocated
583     RetainPtr<WebCoreResourceHandleAsDelegate> protect(self);
584
585     if (!m_handle || !m_handle->client())
586         return nil;
587     
588     // See <rdar://problem/5380697> .  This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
589     if (!redirectResponse)
590         return newRequest;
591     
592     LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:%p", m_handle, connection, [newRequest description], redirectResponse);
593
594     if (redirectResponse && [redirectResponse isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)redirectResponse statusCode] == 307) {
595         String originalMethod = m_handle->request().httpMethod();
596         if (!equalIgnoringCase(originalMethod, String([newRequest HTTPMethod]))) {
597             NSMutableURLRequest *mutableRequest = [newRequest mutableCopy];
598             [mutableRequest setHTTPMethod:originalMethod];
599             newRequest = [mutableRequest autorelease];
600         }
601     }
602
603     CallbackGuard guard;
604     ResourceRequest request = newRequest;
605     m_handle->willSendRequest(request, redirectResponse);
606
607     if (!ResourceHandle::didSendBodyDataDelegateExists()) {
608         // The client may change the request's body stream, in which case we have to re-associate
609         // the handle with the new stream so upload progress callbacks continue to work correctly.
610         NSInputStream* oldBodyStream = [newRequest HTTPBodyStream];
611         NSInputStream* newBodyStream = [request.nsURLRequest() HTTPBodyStream];
612         if (oldBodyStream != newBodyStream) {
613             disassociateStreamWithResourceHandle(oldBodyStream);
614             associateStreamWithResourceHandle(newBodyStream, m_handle);
615         }
616     }
617
618     return request.nsURLRequest();
619 }
620
621 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
622 {
623     UNUSED_PARAM(connection);
624
625     LOG(Network, "Handle %p delegate connectionShouldUseCredentialStorage:%p", m_handle, connection);
626
627     if (!m_handle)
628         return NO;
629
630     CallbackGuard guard;
631     return m_handle->shouldUseCredentialStorage();
632 }
633
634 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
635 {
636     UNUSED_PARAM(connection);
637
638     LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
639
640     if (!m_handle)
641         return;
642     CallbackGuard guard;
643     m_handle->didReceiveAuthenticationChallenge(core(challenge));
644 }
645
646 - (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
647 {
648     UNUSED_PARAM(connection);
649
650     LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge);
651
652     if (!m_handle)
653         return;
654     CallbackGuard guard;
655     m_handle->didCancelAuthenticationChallenge(core(challenge));
656 }
657
658 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
659 {
660     UNUSED_PARAM(connection);
661
662     LOG(Network, "Handle %p delegate connection:%p didReceiveResponse:%p (HTTP status %d, reported MIMEType '%s')", m_handle, connection, r, [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0, [[r MIMEType] UTF8String]);
663
664     if (!m_handle || !m_handle->client())
665         return;
666     CallbackGuard guard;
667
668     [r adjustMIMETypeIfNecessary];
669
670     if ([m_handle->request().nsURLRequest() _propertyForKey:@"ForceHTMLMIMEType"])
671         [r _setMIMEType:@"text/html"];
672
673 #if ENABLE(WML)
674     const KURL& url = [r URL];
675     if (url.isLocalFile()) {
676         // FIXME: Workaround for <rdar://problem/6917571>: The WML file extension ".wml" is not mapped to
677         // the right MIME type, work around that CFNetwork problem, to unbreak WML support for local files.
678         const String& path = url.path();
679   
680         DEFINE_STATIC_LOCAL(const String, wmlExt, (".wml"));
681         if (path.endsWith(wmlExt, false)) {
682             static NSString* defaultMIMETypeString = [(NSString*) defaultMIMEType() retain];
683             if ([[r MIMEType] isEqualToString:defaultMIMETypeString])
684                 [r _setMIMEType:@"text/vnd.wap.wml"];
685         }
686     }
687 #endif
688
689     m_handle->client()->didReceiveResponse(m_handle, r);
690 }
691
692 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
693 {
694     UNUSED_PARAM(connection);
695
696     LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
697
698     if (!m_handle || !m_handle->client())
699         return;
700     // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
701     // However, with today's computers and networking speeds, this won't happen in practice.
702     // Could be an issue with a giant local file.
703     CallbackGuard guard;
704     m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], static_cast<int>(lengthReceived));
705 }
706
707 - (void)connection:(NSURLConnection *)connection willStopBufferingData:(NSData *)data
708 {
709     UNUSED_PARAM(connection);
710
711     LOG(Network, "Handle %p delegate connection:%p willStopBufferingData:%p", m_handle, connection, data);
712
713     if (!m_handle || !m_handle->client())
714         return;
715     // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
716     // However, with today's computers and networking speeds, this won't happen in practice.
717     // Could be an issue with a giant local file.
718     CallbackGuard guard;
719     m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length]));
720 }
721
722 - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
723 {
724     UNUSED_PARAM(connection);
725     UNUSED_PARAM(bytesWritten);
726
727     LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
728
729     if (!m_handle || !m_handle->client())
730         return;
731     CallbackGuard guard;
732     m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
733 }
734
735 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
736 {
737     UNUSED_PARAM(connection);
738
739     LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
740
741     if (!m_handle || !m_handle->client())
742         return;
743     CallbackGuard guard;
744
745     if (!ResourceHandle::didSendBodyDataDelegateExists())
746         disassociateStreamWithResourceHandle([m_handle->request().nsURLRequest() HTTPBodyStream]);
747
748     m_handle->client()->didFinishLoading(m_handle);
749 }
750
751 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
752 {
753     UNUSED_PARAM(connection);
754
755     LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
756
757     if (!m_handle || !m_handle->client())
758         return;
759     CallbackGuard guard;
760
761     if (!ResourceHandle::didSendBodyDataDelegateExists())
762         disassociateStreamWithResourceHandle([m_handle->request().nsURLRequest() HTTPBodyStream]);
763
764     m_handle->client()->didFail(m_handle, error);
765 }
766
767 #ifdef BUILDING_ON_TIGER
768 - (void)_callConnectionWillCacheResponseWithInfo:(NSMutableDictionary *)info
769 {
770     NSURLConnection *connection = [info objectForKey:@"connection"];
771     NSCachedURLResponse *cachedResponse = [info objectForKey:@"cachedResponse"];
772     NSCachedURLResponse *result = [self connection:connection willCacheResponse:cachedResponse];
773     if (result)
774         [info setObject:result forKey:@"result"];
775 }
776 #endif
777
778 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
779 {
780     LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
781
782 #ifdef BUILDING_ON_TIGER
783     // On Tiger CFURLConnection can sometimes call the connection:willCacheResponse: delegate method on
784     // a secondary thread instead of the main thread. If this happens perform the work on the main thread.
785     if (!pthread_main_np()) {
786         NSMutableDictionary *info = [[NSMutableDictionary alloc] init];
787         if (connection)
788             [info setObject:connection forKey:@"connection"];
789         if (cachedResponse)
790             [info setObject:cachedResponse forKey:@"cachedResponse"];
791
792         // Include synchronous url connection's mode as an acceptable run loopmode
793         // <rdar://problem/5511842>
794         NSArray *modes = [[NSArray alloc] initWithObjects:(NSString *)kCFRunLoopCommonModes, @"NSSynchronousURLConnection_PrivateMode", nil];        
795         [self performSelectorOnMainThread:@selector(_callConnectionWillCacheResponseWithInfo:) withObject:info waitUntilDone:YES modes:modes];
796         [modes release];
797
798         NSCachedURLResponse *result = [[info valueForKey:@"result"] retain];
799         [info release];
800
801         return [result autorelease];
802     }
803 #else
804     UNUSED_PARAM(connection);
805 #endif
806
807 #ifndef NDEBUG
808     if (isInitializingConnection)
809         LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (4067625)");
810 #endif
811
812     if (!m_handle || !m_handle->client())
813         return nil;
814
815     CallbackGuard guard;
816     
817     NSCachedURLResponse *newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse);
818     if (newResponse != cachedResponse)
819         return newResponse;
820     
821     CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]);
822         
823     m_handle->client()->willCacheResponse(m_handle, policy);
824
825     if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy])
826         newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response]
827                                                                 data:[newResponse data]
828                                                             userInfo:[newResponse userInfo]
829                                                        storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease];
830
831     return newResponse;
832 }
833
834 - (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
835 {
836     if (!m_handle)
837         return;
838     m_handle->receivedCredential(core(challenge), core(credential));
839 }
840
841 - (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
842 {
843     if (!m_handle)
844         return;
845     m_handle->receivedRequestToContinueWithoutCredential(core(challenge));
846 }
847
848 - (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
849 {
850     if (!m_handle)
851         return;
852     m_handle->receivedCancellation(core(challenge));
853 }
854
855 @end
856
857 #ifndef BUILDING_ON_TIGER
858
859 @implementation WebCoreSynchronousLoader
860
861 - (BOOL)_isDone
862 {
863     return m_isDone;
864 }
865
866 - (void)dealloc
867 {
868     [m_url release];
869     [m_user release];
870     [m_pass release];
871     [m_response release];
872     [m_data release];
873     [m_error release];
874     
875     [super dealloc];
876 }
877
878 - (NSURLRequest *)connection:(NSURLConnection *)unusedConnection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
879 {
880     UNUSED_PARAM(unusedConnection);
881
882     // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests.
883     if (m_url && !protocolHostAndPortAreEqual(m_url, [newRequest URL])) {
884         m_error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:nil];
885         m_isDone = YES;
886         return nil;
887     }
888
889     NSURL *copy = [[newRequest URL] copy];
890     [m_url release];
891     m_url = copy;
892
893     if (redirectResponse) {
894         // Take user/pass out of the URL.
895         [m_user release];
896         [m_pass release];
897         m_user = [[m_url user] copy];
898         m_pass = [[m_url password] copy];
899         if (m_user || m_pass) {
900             ResourceRequest requestWithoutCredentials = newRequest;
901             requestWithoutCredentials.removeCredentials();
902             return requestWithoutCredentials.nsURLRequest();
903         }
904     }
905
906     return newRequest;
907 }
908
909 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)unusedConnection
910 {
911     UNUSED_PARAM(unusedConnection);
912
913     // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden.
914     return m_allowStoredCredentials;
915 }
916
917 - (void)connection:(NSURLConnection *)unusedConnection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
918 {
919     UNUSED_PARAM(unusedConnection);
920
921     if (m_user && m_pass) {
922         NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:m_user
923                                                                    password:m_pass
924                                                                 persistence:NSURLCredentialPersistenceNone];
925         KURL urlToStore;
926         if ([[challenge failureResponse] isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse*)[challenge failureResponse] statusCode] == 401)
927             urlToStore = m_url;
928         CredentialStorage::set(core(credential), core([challenge protectionSpace]), urlToStore);
929         
930         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
931         [credential release];
932         [m_user release];
933         [m_pass release];
934         m_user = 0;
935         m_pass = 0;
936         return;
937     }
938     if ([challenge previousFailureCount] == 0 && m_allowStoredCredentials) {
939         Credential credential = CredentialStorage::get(core([challenge protectionSpace]));
940         ASSERT(credential.persistence() == CredentialPersistenceNone);
941         if (!credential.isEmpty() && credential != m_initialCredential) {
942             [[challenge sender] useCredential:mac(credential) forAuthenticationChallenge:challenge];
943             return;
944         }
945     }
946     // FIXME: The user should be asked for credentials, as in async case.
947     [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
948 }
949
950 - (void)connection:(NSURLConnection *)unusedConnection didReceiveResponse:(NSURLResponse *)response
951 {
952     UNUSED_PARAM(unusedConnection);
953
954     NSURLResponse *r = [response copy];
955     
956     [m_response release];
957     m_response = r;
958 }
959
960 - (void)connection:(NSURLConnection *)unusedConnection didReceiveData:(NSData *)data
961 {
962     UNUSED_PARAM(unusedConnection);
963
964     if (!m_data)
965         m_data = [[NSMutableData alloc] init];
966     
967     [m_data appendData:data];
968 }
969
970 - (void)connectionDidFinishLoading:(NSURLConnection *)unusedConnection
971 {
972     UNUSED_PARAM(unusedConnection);
973
974     m_isDone = YES;
975 }
976
977 - (void)connection:(NSURLConnection *)unusedConnection didFailWithError:(NSError *)error
978 {
979     UNUSED_PARAM(unusedConnection);
980
981     ASSERT(!m_error);
982     
983     m_error = [error retain];
984     m_isDone = YES;
985 }
986
987 - (NSData *)_data
988 {
989     return [[m_data retain] autorelease];
990 }
991
992 - (NSURLResponse *)_response
993 {
994     return [[m_response retain] autorelease];
995 }
996
997 - (NSError *)_error
998 {
999     return [[m_error retain] autorelease];
1000 }
1001
1002 + (NSData *)loadRequest:(NSURLRequest *)request allowStoredCredentials:(BOOL)allowStoredCredentials returningResponse:(NSURLResponse **)response error:(NSError **)error
1003 {
1004     WebCoreSynchronousLoader *delegate = [[WebCoreSynchronousLoader alloc] init];
1005
1006     KURL url([request URL]);
1007     delegate->m_user = [nsStringNilIfEmpty(url.user()) retain];
1008     delegate->m_pass = [nsStringNilIfEmpty(url.pass()) retain];
1009     delegate->m_allowStoredCredentials = allowStoredCredentials;
1010
1011     NSURLConnection *connection;
1012
1013     // Take user/pass out of the URL.
1014     // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
1015     if ((delegate->m_user || delegate->m_pass) && url.protocolInHTTPFamily()) {
1016         ResourceRequest requestWithoutCredentials = request;
1017         requestWithoutCredentials.removeCredentials();
1018         connection = [[NSURLConnection alloc] initWithRequest:requestWithoutCredentials.nsURLRequest() delegate:delegate startImmediately:NO];
1019     } else {
1020         // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
1021         // try and reuse the credential preemptively, as allowed by RFC 2617.
1022         ResourceRequest requestWithInitialCredentials = request;
1023         if (allowStoredCredentials && url.protocolInHTTPFamily())
1024             delegate->m_initialCredential = CredentialStorage::getDefaultAuthenticationCredential(url);
1025             
1026         if (!delegate->m_initialCredential.isEmpty()) {
1027             // Apply basic auth header
1028             String unencoded = delegate->m_initialCredential.user() + ":" + delegate->m_initialCredential.password();
1029             CString encoded = unencoded.utf8().base64Encode();
1030             String authHeader = String::format("Basic %s", encoded.data());
1031             requestWithInitialCredentials.addHTTPHeaderField("Authorization", authHeader);
1032         }
1033         connection = [[NSURLConnection alloc] initWithRequest:requestWithInitialCredentials.nsURLRequest() delegate:delegate startImmediately:NO];
1034     }
1035
1036     [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode];
1037     [connection start];
1038     
1039     while (![delegate _isDone])
1040         [[NSRunLoop currentRunLoop] runMode:WebCoreSynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
1041
1042     NSData *data = [delegate _data];
1043     *response = [delegate _response];
1044     *error = [delegate _error];
1045     
1046     [connection cancel];
1047     
1048     [connection release];
1049     [delegate release];
1050     
1051     return data;
1052 }
1053
1054 @end
1055
1056 #endif