1/*
2 * Copyright (C) 2006 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 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 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 "ResourceResponse.h"
28
29#import "HTTPParsers.h"
30#import "WebCoreURLResponse.h"
31#import "WebCoreSystemInterface.h"
32#import <Foundation/Foundation.h>
33#import <limits>
34#import <wtf/StdLibExtras.h>
35
36@interface NSURLResponse (WebNSURLResponseDetails)
37- (NSTimeInterval)_calculatedExpiration;
38- (id)_initWithCFURLResponse:(CFURLResponseRef)response;
39- (CFURLResponseRef) _CFURLResponse;
40+ (id)_responseWithCFURLResponse:(CFURLResponseRef)response;
41@end
42
43
44namespace WebCore {
45
46void ResourceResponse::initNSURLResponse() const
47{
48    // Work around a mistake in the NSURLResponse class - <rdar://problem/6875219>.
49    // The init function takes an NSInteger, even though the accessor returns a long long.
50    // For values that won't fit in an NSInteger, pass -1 instead.
51    NSInteger expectedContentLength;
52    if (m_expectedContentLength < 0 || m_expectedContentLength > std::numeric_limits<NSInteger>::max())
53        expectedContentLength = -1;
54    else
55        expectedContentLength = static_cast<NSInteger>(m_expectedContentLength);
56
57    // FIXME: This creates a very incomplete NSURLResponse, which does not even have a status code.
58
59    m_nsResponse = adoptNS([[NSURLResponse alloc] initWithURL:m_url MIMEType:m_mimeType expectedContentLength:expectedContentLength textEncodingName:m_textEncodingName]);
60}
61
62#if USE(CFNETWORK)
63
64NSURLResponse *ResourceResponse::nsURLResponse() const
65{
66    if (!m_nsResponse && !m_cfResponse && !m_isNull) {
67        initNSURLResponse();
68        m_cfResponse = [m_nsResponse.get() _CFURLResponse];
69        return m_nsResponse.get();
70    }
71
72    if (!m_cfResponse)
73        return nil;
74
75    if (!m_nsResponse)
76        m_nsResponse = [NSURLResponse _responseWithCFURLResponse:m_cfResponse.get()];
77
78    return m_nsResponse.get();
79}
80
81ResourceResponse::ResourceResponse(NSURLResponse* nsResponse)
82    : m_initLevel(Uninitialized)
83    , m_platformResponseIsUpToDate(true)
84    , m_cfResponse([nsResponse _CFURLResponse])
85    , m_nsResponse(nsResponse)
86{
87    m_isNull = !nsResponse;
88}
89
90#else
91
92static NSString* const commonHeaderFields[] = {
93    @"Age", @"Cache-Control", @"Content-Type", @"Date", @"Etag", @"Expires", @"Last-Modified", @"Pragma"
94};
95
96NSURLResponse *ResourceResponse::nsURLResponse() const
97{
98    if (!m_nsResponse && !m_isNull)
99        initNSURLResponse();
100    return m_nsResponse.get();
101}
102
103void ResourceResponse::platformLazyInit(InitLevel initLevel)
104{
105    if (m_initLevel >= initLevel)
106        return;
107
108    if (m_isNull || !m_nsResponse)
109        return;
110
111    if (m_initLevel < CommonFieldsOnly && initLevel >= CommonFieldsOnly) {
112        NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
113
114        m_httpHeaderFields.clear();
115        m_url = [m_nsResponse.get() URL];
116        m_mimeType = [m_nsResponse.get() MIMEType];
117        m_expectedContentLength = [m_nsResponse.get() expectedContentLength];
118        m_textEncodingName = [m_nsResponse.get() textEncodingName];
119
120        // Workaround for <rdar://problem/8757088>, can be removed once that is fixed.
121        unsigned textEncodingNameLength = m_textEncodingName.length();
122        if (textEncodingNameLength >= 2 && m_textEncodingName[0U] == '"' && m_textEncodingName[textEncodingNameLength - 1] == '"')
123            m_textEncodingName = m_textEncodingName.string().substring(1, textEncodingNameLength - 2);
124
125        if ([m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]]) {
126            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)m_nsResponse.get();
127
128            m_httpStatusCode = [httpResponse statusCode];
129
130            NSDictionary *headers = [httpResponse allHeaderFields];
131
132            for (unsigned i = 0; i < WTF_ARRAY_LENGTH(commonHeaderFields); ++i) {
133                if (NSString* headerValue = [headers objectForKey:commonHeaderFields[i]])
134                    m_httpHeaderFields.set(String(commonHeaderFields[i]), headerValue);
135            }
136        } else
137            m_httpStatusCode = 0;
138
139        [pool drain];
140    }
141
142    if (m_initLevel < CommonAndUncommonFields && initLevel >= CommonAndUncommonFields) {
143        if ([m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]]) {
144            NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
145
146            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)m_nsResponse.get();
147
148            RetainPtr<NSString> httpStatusLine = adoptNS(wkCopyNSURLResponseStatusLine(m_nsResponse.get()));
149            if (httpStatusLine)
150                m_httpStatusText = extractReasonPhraseFromHTTPStatusLine(httpStatusLine.get());
151            else
152                m_httpStatusText = "OK";
153
154            NSDictionary *headers = [httpResponse allHeaderFields];
155            NSEnumerator *e = [headers keyEnumerator];
156            while (NSString *name = [e nextObject])
157                m_httpHeaderFields.set(String(name), [headers objectForKey:name]);
158
159            [pool drain];
160        }
161    }
162
163    if (m_initLevel < AllFields && initLevel >= AllFields)
164        m_suggestedFilename = [m_nsResponse.get() suggestedFilename];
165
166    m_initLevel = initLevel;
167}
168
169
170bool ResourceResponse::platformCompare(const ResourceResponse& a, const ResourceResponse& b)
171{
172    return a.nsURLResponse() == b.nsURLResponse();
173}
174
175#endif // USE(CFNETWORK)
176
177#if PLATFORM(COCOA) || USE(CFNETWORK)
178
179void ResourceResponse::setCertificateChain(CFArrayRef certificateChain)
180{
181    ASSERT(!wkCopyNSURLResponseCertificateChain(nsURLResponse()));
182    m_externalCertificateChain = certificateChain;
183}
184
185RetainPtr<CFArrayRef> ResourceResponse::certificateChain() const
186{
187    if (m_externalCertificateChain)
188        return m_externalCertificateChain;
189
190    return adoptCF(wkCopyNSURLResponseCertificateChain(nsURLResponse()));
191}
192
193#endif // PLATFORM(COCOA) || USE(CFNETWORK)
194
195} // namespace WebCore
196
197