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