1/*
2 * Copyright (C) 2006, 2007 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#include "WebURLResponse.h"
28
29#include "WebKitDLL.h"
30#include "WebKit.h"
31
32#include "COMPropertyBag.h"
33#include "MarshallingHelpers.h"
34
35#if USE(CFNETWORK)
36#include <WebKitSystemInterface/WebKitSystemInterface.h>
37#endif
38
39#include <wtf/platform.h>
40#include <WebCore/BString.h>
41#include <WebCore/KURL.h>
42#include <WebCore/LocalizedStrings.h>
43#include <WebCore/ResourceHandle.h>
44#include <shlobj.h>
45#include <shlwapi.h>
46#include <wchar.h>
47
48using namespace WebCore;
49
50static String CFHTTPMessageCopyLocalizedShortDescriptionForStatusCode(CFIndex statusCode)
51{
52    String result;
53    if (statusCode < 100 || statusCode >= 600)
54        result = WEB_UI_STRING("server error", "HTTP result code string");
55    else if (statusCode >= 100 && statusCode <= 199) {
56        switch (statusCode) {
57            case 100:
58                result = WEB_UI_STRING("continue", "HTTP result code string");
59                break;
60            case 101:
61                result = WEB_UI_STRING("switching protocols", "HTTP result code string");
62                break;
63            default:
64                result = WEB_UI_STRING("informational", "HTTP result code string");
65                break;
66        }
67    } else if (statusCode >= 200 && statusCode <= 299) {
68        switch (statusCode) {
69            case 200:
70                result = WEB_UI_STRING("no error", "HTTP result code string");
71                break;
72            case 201:
73                result = WEB_UI_STRING("created", "HTTP result code string");
74                break;
75            case 202:
76                result = WEB_UI_STRING("accepted", "HTTP result code string");
77                break;
78            case 203:
79                result = WEB_UI_STRING("non-authoritative information", "HTTP result code string");
80                break;
81            case 204:
82                result = WEB_UI_STRING("no content", "HTTP result code string");
83                break;
84            case 205:
85                result = WEB_UI_STRING("reset content", "HTTP result code string");
86                break;
87            case 206:
88                result = WEB_UI_STRING("partial content", "HTTP result code string");
89                break;
90            default:
91                result = WEB_UI_STRING("success", "HTTP result code string");
92                break;
93        }
94    } else if (statusCode >= 300 && statusCode <= 399) {
95        switch (statusCode) {
96            case 300:
97                result = WEB_UI_STRING("multiple choices", "HTTP result code string");
98                break;
99            case 301:
100                result = WEB_UI_STRING("moved permanently", "HTTP result code string");
101                break;
102            case 302:
103                result = WEB_UI_STRING("found", "HTTP result code string");
104                break;
105            case 303:
106                result = WEB_UI_STRING("see other", "HTTP result code string");
107                break;
108            case 304:
109                result = WEB_UI_STRING("not modified", "HTTP result code string");
110                break;
111            case 305:
112                result = WEB_UI_STRING("needs proxy", "HTTP result code string");
113                break;
114            case 307:
115                result = WEB_UI_STRING("temporarily redirected", "HTTP result code string");
116                break;
117            case 306:   // 306 status code unused in HTTP
118            default:
119                result = WEB_UI_STRING("redirected", "HTTP result code string");
120                break;
121        }
122    } else if (statusCode >= 400 && statusCode <= 499) {
123        switch (statusCode) {
124            case 400:
125                result = WEB_UI_STRING("bad request", "HTTP result code string");
126                break;
127            case 401:
128                result = WEB_UI_STRING("unauthorized", "HTTP result code string");
129                break;
130            case 402:
131                result = WEB_UI_STRING("payment required", "HTTP result code string");
132                break;
133            case 403:
134                result = WEB_UI_STRING("forbidden", "HTTP result code string");
135                break;
136            case 404:
137                result = WEB_UI_STRING("not found", "HTTP result code string");
138                break;
139            case 405:
140                result = WEB_UI_STRING("method not allowed", "HTTP result code string");
141                break;
142            case 406:
143                result = WEB_UI_STRING("unacceptable", "HTTP result code string");
144                break;
145            case 407:
146                result = WEB_UI_STRING("proxy authentication required", "HTTP result code string");
147                break;
148            case 408:
149                result = WEB_UI_STRING("request timed out", "HTTP result code string");
150                break;
151            case 409:
152                result = WEB_UI_STRING("conflict", "HTTP result code string");
153                break;
154            case 410:
155                result = WEB_UI_STRING("no longer exists", "HTTP result code string");
156                break;
157            case 411:
158                result = WEB_UI_STRING("length required", "HTTP result code string");
159                break;
160            case 412:
161                result = WEB_UI_STRING("precondition failed", "HTTP result code string");
162                break;
163            case 413:
164                result = WEB_UI_STRING("request too large", "HTTP result code string");
165                break;
166            case 414:
167                result = WEB_UI_STRING("requested URL too long", "HTTP result code string");
168                break;
169            case 415:
170                result = WEB_UI_STRING("unsupported media type", "HTTP result code string");
171                break;
172            case 416:
173                result = WEB_UI_STRING("requested range not satisfiable", "HTTP result code string");
174                break;
175            case 417:
176                result = WEB_UI_STRING("expectation failed", "HTTP result code string");
177                break;
178            default:
179                result = WEB_UI_STRING("client error", "HTTP result code string");
180                break;
181        }
182    } else if (statusCode >= 500 && statusCode <= 599) {
183        switch (statusCode) {
184            case 500:
185                result = WEB_UI_STRING("internal server error", "HTTP result code string");
186                break;
187            case 501:
188                result = WEB_UI_STRING("unimplemented", "HTTP result code string");
189                break;
190            case 502:
191                result = WEB_UI_STRING("bad gateway", "HTTP result code string");
192                break;
193            case 503:
194                result = WEB_UI_STRING("service unavailable", "HTTP result code string");
195                break;
196            case 504:
197                result = WEB_UI_STRING("gateway timed out", "HTTP result code string");
198                break;
199            case 505:
200                result = WEB_UI_STRING("unsupported version", "HTTP result code string");
201                break;
202            default:
203                result = WEB_UI_STRING("server error", "HTTP result code string");
204                break;
205        }
206    }
207    return result;
208}
209
210// IWebURLResponse ----------------------------------------------------------------
211
212WebURLResponse::WebURLResponse()
213    :m_refCount(0)
214{
215    gClassCount++;
216    gClassNameCount.add("WebURLResponse");
217}
218
219WebURLResponse::~WebURLResponse()
220{
221    gClassCount--;
222    gClassNameCount.remove("WebURLResponse");
223}
224
225WebURLResponse* WebURLResponse::createInstance()
226{
227    WebURLResponse* instance = new WebURLResponse();
228    // fake an http response - so it has the IWebHTTPURLResponse interface
229    instance->m_response = ResourceResponse(KURL(ParsedURLString, "http://"), String(), 0, String(), String());
230    instance->AddRef();
231    return instance;
232}
233
234WebURLResponse* WebURLResponse::createInstance(const ResourceResponse& response)
235{
236    if (response.isNull())
237        return 0;
238
239    WebURLResponse* instance = new WebURLResponse();
240    instance->AddRef();
241    instance->m_response = response;
242
243    return instance;
244}
245
246// IUnknown -------------------------------------------------------------------
247
248HRESULT STDMETHODCALLTYPE WebURLResponse::QueryInterface(REFIID riid, void** ppvObject)
249{
250    *ppvObject = 0;
251    if (IsEqualGUID(riid, IID_IUnknown))
252        *ppvObject = static_cast<IWebURLResponse*>(this);
253    else if (IsEqualGUID(riid, __uuidof(this)))
254        *ppvObject = this;
255    else if (IsEqualGUID(riid, IID_IWebURLResponse))
256        *ppvObject = static_cast<IWebURLResponse*>(this);
257    else if (IsEqualGUID(riid, IID_IWebURLResponsePrivate))
258        *ppvObject = static_cast<IWebURLResponsePrivate*>(this);
259    else if (m_response.isHTTP() && IsEqualGUID(riid, IID_IWebHTTPURLResponse))
260        *ppvObject = static_cast<IWebHTTPURLResponse*>(this);
261    else
262        return E_NOINTERFACE;
263
264    AddRef();
265    return S_OK;
266}
267
268ULONG STDMETHODCALLTYPE WebURLResponse::AddRef(void)
269{
270    return ++m_refCount;
271}
272
273ULONG STDMETHODCALLTYPE WebURLResponse::Release(void)
274{
275    ULONG newRef = --m_refCount;
276    if (!newRef)
277        delete(this);
278
279    return newRef;
280}
281
282// IWebURLResponse --------------------------------------------------------------------
283
284HRESULT STDMETHODCALLTYPE WebURLResponse::expectedContentLength(
285    /* [retval][out] */ long long* result)
286{
287    *result = m_response.expectedContentLength();
288    return S_OK;
289}
290
291HRESULT STDMETHODCALLTYPE WebURLResponse::initWithURL(
292    /* [in] */ BSTR url,
293    /* [in] */ BSTR mimeType,
294    /* [in] */ int expectedContentLength,
295    /* [in] */ BSTR textEncodingName)
296{
297    m_response = ResourceResponse(MarshallingHelpers::BSTRToKURL(url), String(mimeType), expectedContentLength, String(textEncodingName), String());
298    return S_OK;
299}
300
301HRESULT STDMETHODCALLTYPE WebURLResponse::MIMEType(
302    /* [retval][out] */ BSTR* result)
303{
304    BString mimeType(m_response.mimeType());
305    *result = mimeType.release();
306    if (!m_response.mimeType().isNull() && !*result)
307        return E_OUTOFMEMORY;
308
309    return S_OK;
310}
311
312HRESULT STDMETHODCALLTYPE WebURLResponse::suggestedFilename(
313    /* [retval][out] */ BSTR* result)
314{
315    if (!result) {
316        ASSERT_NOT_REACHED();
317        return E_POINTER;
318    }
319
320    *result = 0;
321
322    if (m_response.url().isEmpty())
323        return E_FAIL;
324
325    *result = BString(m_response.suggestedFilename()).release();
326    return S_OK;
327}
328
329HRESULT STDMETHODCALLTYPE WebURLResponse::textEncodingName(
330    /* [retval][out] */ BSTR* result)
331{
332    if (!result)
333        return E_INVALIDARG;
334
335    BString textEncodingName(m_response.textEncodingName());
336    *result = textEncodingName.release();
337    if (!m_response.textEncodingName().isNull() && !*result)
338        return E_OUTOFMEMORY;
339
340    return S_OK;
341}
342
343HRESULT STDMETHODCALLTYPE WebURLResponse::URL(
344    /* [retval][out] */ BSTR* result)
345{
346    if (!result)
347        return E_INVALIDARG;
348
349    BString url(m_response.url().string());
350    *result = url.release();
351    if (!m_response.url().isEmpty() && !*result)
352        return E_OUTOFMEMORY;
353
354    return S_OK;
355}
356
357// IWebHTTPURLResponse --------------------------------------------------------
358
359HRESULT STDMETHODCALLTYPE WebURLResponse::allHeaderFields(
360    /* [retval][out] */ IPropertyBag** headerFields)
361{
362    ASSERT(m_response.isHTTP());
363
364    *headerFields = COMPropertyBag<String, AtomicString, CaseFoldingHash>::createInstance(m_response.httpHeaderFields());
365    return S_OK;
366}
367
368HRESULT STDMETHODCALLTYPE WebURLResponse::localizedStringForStatusCode(
369    /* [in] */ int statusCode,
370    /* [retval][out] */ BSTR* statusString)
371{
372    ASSERT(m_response.isHTTP());
373    if (statusString)
374        *statusString = 0;
375    String statusText = CFHTTPMessageCopyLocalizedShortDescriptionForStatusCode(statusCode);
376    if (!statusText)
377        return E_FAIL;
378    if (statusString)
379        *statusString = BString(statusText).release();
380    return S_OK;
381}
382
383HRESULT STDMETHODCALLTYPE WebURLResponse::statusCode(
384    /* [retval][out] */ int* statusCode)
385{
386    ASSERT(m_response.isHTTP());
387    if (statusCode)
388        *statusCode = m_response.httpStatusCode();
389    return S_OK;
390}
391
392HRESULT STDMETHODCALLTYPE WebURLResponse::isAttachment(
393    /* [retval][out] */ BOOL *attachment)
394{
395    *attachment = m_response.isAttachment();
396    return S_OK;
397}
398
399
400HRESULT STDMETHODCALLTYPE WebURLResponse::sslPeerCertificate(
401    /* [retval][out] */ OLE_HANDLE* result)
402{
403    if (!result)
404        return E_POINTER;
405    *result = 0;
406
407#if USE(CFNETWORK)
408    CFDictionaryRef dict = certificateDictionary();
409    if (!dict)
410        return E_FAIL;
411    void* data = wkGetSSLPeerCertificateDataBytePtr(dict);
412    if (!data)
413        return E_FAIL;
414    *result = (OLE_HANDLE)(ULONG64)data;
415#endif
416
417    return *result ? S_OK : E_FAIL;
418}
419
420// WebURLResponse -------------------------------------------------------------
421
422HRESULT WebURLResponse::suggestedFileExtension(BSTR *result)
423{
424    if (!result)
425        return E_POINTER;
426
427    *result = 0;
428
429    if (m_response.mimeType().isEmpty())
430        return E_FAIL;
431
432    BString mimeType(m_response.mimeType());
433    HKEY key;
434    LONG err = RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("MIME\\Database\\Content Type"), 0, KEY_QUERY_VALUE, &key);
435    if (!err) {
436        HKEY subKey;
437        err = RegOpenKeyEx(key, mimeType, 0, KEY_QUERY_VALUE, &subKey);
438        if (!err) {
439            DWORD keyType = REG_SZ;
440            WCHAR extension[MAX_PATH];
441            DWORD keySize = sizeof(extension)/sizeof(extension[0]);
442            err = RegQueryValueEx(subKey, TEXT("Extension"), 0, &keyType, (LPBYTE)extension, &keySize);
443            if (!err && keyType != REG_SZ)
444                err = ERROR_INVALID_DATA;
445            if (err) {
446                // fallback handlers
447                if (!wcscmp(mimeType, L"text/html")) {
448                    wcscpy(extension, L".html");
449                    err = 0;
450                } else if (!wcscmp(mimeType, L"application/xhtml+xml")) {
451                    wcscpy(extension, L".xhtml");
452                    err = 0;
453                } else if (!wcscmp(mimeType, L"image/svg+xml")) {
454                    wcscpy(extension, L".svg");
455                    err = 0;
456                }
457            }
458            if (!err) {
459                *result = SysAllocString(extension);
460                if (!*result)
461                    err = ERROR_OUTOFMEMORY;
462            }
463            RegCloseKey(subKey);
464        }
465        RegCloseKey(key);
466    }
467
468    return HRESULT_FROM_WIN32(err);
469}
470
471const ResourceResponse& WebURLResponse::resourceResponse() const
472{
473    return m_response;
474}
475
476#if USE(CFNETWORK)
477CFDictionaryRef WebURLResponse::certificateDictionary() const
478{
479    if (m_SSLCertificateInfo)
480        return m_SSLCertificateInfo.get();
481
482    CFURLResponseRef cfResponse = m_response.cfURLResponse();
483    if (!cfResponse)
484        return 0;
485    m_SSLCertificateInfo = wkGetSSLCertificateInfo(cfResponse);
486    return m_SSLCertificateInfo.get();
487}
488#endif
489