/* * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WebURLResponse.h" #include "WebKitDLL.h" #include "WebKit.h" #include "COMPropertyBag.h" #include "MarshallingHelpers.h" #if USE(CFNETWORK) #include <WebKitSystemInterface/WebKitSystemInterface.h> #endif #include <wtf/platform.h> #include <WebCore/BString.h> #include <WebCore/KURL.h> #include <WebCore/LocalizedStrings.h> #include <WebCore/ResourceHandle.h> #include <shlobj.h> #include <shlwapi.h> #include <wchar.h> using namespace WebCore; static String CFHTTPMessageCopyLocalizedShortDescriptionForStatusCode(CFIndex statusCode) { String result; if (statusCode < 100 || statusCode >= 600) result = WEB_UI_STRING("server error", "HTTP result code string"); else if (statusCode >= 100 && statusCode <= 199) { switch (statusCode) { case 100: result = WEB_UI_STRING("continue", "HTTP result code string"); break; case 101: result = WEB_UI_STRING("switching protocols", "HTTP result code string"); break; default: result = WEB_UI_STRING("informational", "HTTP result code string"); break; } } else if (statusCode >= 200 && statusCode <= 299) { switch (statusCode) { case 200: result = WEB_UI_STRING("no error", "HTTP result code string"); break; case 201: result = WEB_UI_STRING("created", "HTTP result code string"); break; case 202: result = WEB_UI_STRING("accepted", "HTTP result code string"); break; case 203: result = WEB_UI_STRING("non-authoritative information", "HTTP result code string"); break; case 204: result = WEB_UI_STRING("no content", "HTTP result code string"); break; case 205: result = WEB_UI_STRING("reset content", "HTTP result code string"); break; case 206: result = WEB_UI_STRING("partial content", "HTTP result code string"); break; default: result = WEB_UI_STRING("success", "HTTP result code string"); break; } } else if (statusCode >= 300 && statusCode <= 399) { switch (statusCode) { case 300: result = WEB_UI_STRING("multiple choices", "HTTP result code string"); break; case 301: result = WEB_UI_STRING("moved permanently", "HTTP result code string"); break; case 302: result = WEB_UI_STRING("found", "HTTP result code string"); break; case 303: result = WEB_UI_STRING("see other", "HTTP result code string"); break; case 304: result = WEB_UI_STRING("not modified", "HTTP result code string"); break; case 305: result = WEB_UI_STRING("needs proxy", "HTTP result code string"); break; case 307: result = WEB_UI_STRING("temporarily redirected", "HTTP result code string"); break; case 306: // 306 status code unused in HTTP default: result = WEB_UI_STRING("redirected", "HTTP result code string"); break; } } else if (statusCode >= 400 && statusCode <= 499) { switch (statusCode) { case 400: result = WEB_UI_STRING("bad request", "HTTP result code string"); break; case 401: result = WEB_UI_STRING("unauthorized", "HTTP result code string"); break; case 402: result = WEB_UI_STRING("payment required", "HTTP result code string"); break; case 403: result = WEB_UI_STRING("forbidden", "HTTP result code string"); break; case 404: result = WEB_UI_STRING("not found", "HTTP result code string"); break; case 405: result = WEB_UI_STRING("method not allowed", "HTTP result code string"); break; case 406: result = WEB_UI_STRING("unacceptable", "HTTP result code string"); break; case 407: result = WEB_UI_STRING("proxy authentication required", "HTTP result code string"); break; case 408: result = WEB_UI_STRING("request timed out", "HTTP result code string"); break; case 409: result = WEB_UI_STRING("conflict", "HTTP result code string"); break; case 410: result = WEB_UI_STRING("no longer exists", "HTTP result code string"); break; case 411: result = WEB_UI_STRING("length required", "HTTP result code string"); break; case 412: result = WEB_UI_STRING("precondition failed", "HTTP result code string"); break; case 413: result = WEB_UI_STRING("request too large", "HTTP result code string"); break; case 414: result = WEB_UI_STRING("requested URL too long", "HTTP result code string"); break; case 415: result = WEB_UI_STRING("unsupported media type", "HTTP result code string"); break; case 416: result = WEB_UI_STRING("requested range not satisfiable", "HTTP result code string"); break; case 417: result = WEB_UI_STRING("expectation failed", "HTTP result code string"); break; default: result = WEB_UI_STRING("client error", "HTTP result code string"); break; } } else if (statusCode >= 500 && statusCode <= 599) { switch (statusCode) { case 500: result = WEB_UI_STRING("internal server error", "HTTP result code string"); break; case 501: result = WEB_UI_STRING("unimplemented", "HTTP result code string"); break; case 502: result = WEB_UI_STRING("bad gateway", "HTTP result code string"); break; case 503: result = WEB_UI_STRING("service unavailable", "HTTP result code string"); break; case 504: result = WEB_UI_STRING("gateway timed out", "HTTP result code string"); break; case 505: result = WEB_UI_STRING("unsupported version", "HTTP result code string"); break; default: result = WEB_UI_STRING("server error", "HTTP result code string"); break; } } return result; } // IWebURLResponse ---------------------------------------------------------------- WebURLResponse::WebURLResponse() :m_refCount(0) { gClassCount++; gClassNameCount.add("WebURLResponse"); } WebURLResponse::~WebURLResponse() { gClassCount--; gClassNameCount.remove("WebURLResponse"); } WebURLResponse* WebURLResponse::createInstance() { WebURLResponse* instance = new WebURLResponse(); // fake an http response - so it has the IWebHTTPURLResponse interface instance->m_response = ResourceResponse(KURL(ParsedURLString, "http://"), String(), 0, String(), String()); instance->AddRef(); return instance; } WebURLResponse* WebURLResponse::createInstance(const ResourceResponse& response) { if (response.isNull()) return 0; WebURLResponse* instance = new WebURLResponse(); instance->AddRef(); instance->m_response = response; return instance; } // IUnknown ------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE WebURLResponse::QueryInterface(REFIID riid, void** ppvObject) { *ppvObject = 0; if (IsEqualGUID(riid, IID_IUnknown)) *ppvObject = static_cast<IWebURLResponse*>(this); else if (IsEqualGUID(riid, __uuidof(this))) *ppvObject = this; else if (IsEqualGUID(riid, IID_IWebURLResponse)) *ppvObject = static_cast<IWebURLResponse*>(this); else if (IsEqualGUID(riid, IID_IWebURLResponsePrivate)) *ppvObject = static_cast<IWebURLResponsePrivate*>(this); else if (m_response.isHTTP() && IsEqualGUID(riid, IID_IWebHTTPURLResponse)) *ppvObject = static_cast<IWebHTTPURLResponse*>(this); else return E_NOINTERFACE; AddRef(); return S_OK; } ULONG STDMETHODCALLTYPE WebURLResponse::AddRef(void) { return ++m_refCount; } ULONG STDMETHODCALLTYPE WebURLResponse::Release(void) { ULONG newRef = --m_refCount; if (!newRef) delete(this); return newRef; } // IWebURLResponse -------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE WebURLResponse::expectedContentLength( /* [retval][out] */ long long* result) { *result = m_response.expectedContentLength(); return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::initWithURL( /* [in] */ BSTR url, /* [in] */ BSTR mimeType, /* [in] */ int expectedContentLength, /* [in] */ BSTR textEncodingName) { m_response = ResourceResponse(MarshallingHelpers::BSTRToKURL(url), String(mimeType), expectedContentLength, String(textEncodingName), String()); return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::MIMEType( /* [retval][out] */ BSTR* result) { BString mimeType(m_response.mimeType()); *result = mimeType.release(); if (!m_response.mimeType().isNull() && !*result) return E_OUTOFMEMORY; return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::suggestedFilename( /* [retval][out] */ BSTR* result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } *result = 0; if (m_response.url().isEmpty()) return E_FAIL; *result = BString(m_response.suggestedFilename()).release(); return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::textEncodingName( /* [retval][out] */ BSTR* result) { if (!result) return E_INVALIDARG; BString textEncodingName(m_response.textEncodingName()); *result = textEncodingName.release(); if (!m_response.textEncodingName().isNull() && !*result) return E_OUTOFMEMORY; return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::URL( /* [retval][out] */ BSTR* result) { if (!result) return E_INVALIDARG; BString url(m_response.url().string()); *result = url.release(); if (!m_response.url().isEmpty() && !*result) return E_OUTOFMEMORY; return S_OK; } // IWebHTTPURLResponse -------------------------------------------------------- HRESULT STDMETHODCALLTYPE WebURLResponse::allHeaderFields( /* [retval][out] */ IPropertyBag** headerFields) { ASSERT(m_response.isHTTP()); *headerFields = COMPropertyBag<String, AtomicString, CaseFoldingHash>::createInstance(m_response.httpHeaderFields()); return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::localizedStringForStatusCode( /* [in] */ int statusCode, /* [retval][out] */ BSTR* statusString) { ASSERT(m_response.isHTTP()); if (statusString) *statusString = 0; String statusText = CFHTTPMessageCopyLocalizedShortDescriptionForStatusCode(statusCode); if (!statusText) return E_FAIL; if (statusString) *statusString = BString(statusText).release(); return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::statusCode( /* [retval][out] */ int* statusCode) { ASSERT(m_response.isHTTP()); if (statusCode) *statusCode = m_response.httpStatusCode(); return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::isAttachment( /* [retval][out] */ BOOL *attachment) { *attachment = m_response.isAttachment(); return S_OK; } HRESULT STDMETHODCALLTYPE WebURLResponse::sslPeerCertificate( /* [retval][out] */ OLE_HANDLE* result) { if (!result) return E_POINTER; *result = 0; #if USE(CFNETWORK) CFDictionaryRef dict = certificateDictionary(); if (!dict) return E_FAIL; void* data = wkGetSSLPeerCertificateDataBytePtr(dict); if (!data) return E_FAIL; *result = (OLE_HANDLE)(ULONG64)data; #endif return *result ? S_OK : E_FAIL; } // WebURLResponse ------------------------------------------------------------- HRESULT WebURLResponse::suggestedFileExtension(BSTR *result) { if (!result) return E_POINTER; *result = 0; if (m_response.mimeType().isEmpty()) return E_FAIL; BString mimeType(m_response.mimeType()); HKEY key; LONG err = RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("MIME\\Database\\Content Type"), 0, KEY_QUERY_VALUE, &key); if (!err) { HKEY subKey; err = RegOpenKeyEx(key, mimeType, 0, KEY_QUERY_VALUE, &subKey); if (!err) { DWORD keyType = REG_SZ; WCHAR extension[MAX_PATH]; DWORD keySize = sizeof(extension)/sizeof(extension[0]); err = RegQueryValueEx(subKey, TEXT("Extension"), 0, &keyType, (LPBYTE)extension, &keySize); if (!err && keyType != REG_SZ) err = ERROR_INVALID_DATA; if (err) { // fallback handlers if (!wcscmp(mimeType, L"text/html")) { wcscpy(extension, L".html"); err = 0; } else if (!wcscmp(mimeType, L"application/xhtml+xml")) { wcscpy(extension, L".xhtml"); err = 0; } else if (!wcscmp(mimeType, L"image/svg+xml")) { wcscpy(extension, L".svg"); err = 0; } } if (!err) { *result = SysAllocString(extension); if (!*result) err = ERROR_OUTOFMEMORY; } RegCloseKey(subKey); } RegCloseKey(key); } return HRESULT_FROM_WIN32(err); } const ResourceResponse& WebURLResponse::resourceResponse() const { return m_response; } #if USE(CFNETWORK) CFDictionaryRef WebURLResponse::certificateDictionary() const { if (m_SSLCertificateInfo) return m_SSLCertificateInfo.get(); CFURLResponseRef cfResponse = m_response.cfURLResponse(); if (!cfResponse) return 0; m_SSLCertificateInfo = wkGetSSLCertificateInfo(cfResponse); return m_SSLCertificateInfo.get(); } #endif