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
48 using namespace WebCore;
49
CFHTTPMessageCopyLocalizedShortDescriptionForStatusCode(CFIndex statusCode)50 static 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
WebURLResponse()212 WebURLResponse::WebURLResponse()
213 :m_refCount(0)
214 {
215 gClassCount++;
216 gClassNameCount.add("WebURLResponse");
217 }
218
~WebURLResponse()219 WebURLResponse::~WebURLResponse()
220 {
221 gClassCount--;
222 gClassNameCount.remove("WebURLResponse");
223 }
224
createInstance()225 WebURLResponse* 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
createInstance(const ResourceResponse & response)234 WebURLResponse* 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
QueryInterface(REFIID riid,void ** ppvObject)248 HRESULT 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
AddRef(void)268 ULONG STDMETHODCALLTYPE WebURLResponse::AddRef(void)
269 {
270 return ++m_refCount;
271 }
272
Release(void)273 ULONG 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
expectedContentLength(long long * result)284 HRESULT STDMETHODCALLTYPE WebURLResponse::expectedContentLength(
285 /* [retval][out] */ long long* result)
286 {
287 *result = m_response.expectedContentLength();
288 return S_OK;
289 }
290
initWithURL(BSTR url,BSTR mimeType,int expectedContentLength,BSTR textEncodingName)291 HRESULT 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
MIMEType(BSTR * result)301 HRESULT 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
suggestedFilename(BSTR * result)312 HRESULT 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
textEncodingName(BSTR * result)329 HRESULT 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
URL(BSTR * result)343 HRESULT 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
allHeaderFields(IPropertyBag ** headerFields)359 HRESULT 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
localizedStringForStatusCode(int statusCode,BSTR * statusString)368 HRESULT 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
statusCode(int * statusCode)383 HRESULT 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
isAttachment(BOOL * attachment)392 HRESULT STDMETHODCALLTYPE WebURLResponse::isAttachment(
393 /* [retval][out] */ BOOL *attachment)
394 {
395 *attachment = m_response.isAttachment();
396 return S_OK;
397 }
398
399
sslPeerCertificate(OLE_HANDLE * result)400 HRESULT 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
suggestedFileExtension(BSTR * result)422 HRESULT 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
resourceResponse() const471 const ResourceResponse& WebURLResponse::resourceResponse() const
472 {
473 return m_response;
474 }
475
476 #if USE(CFNETWORK)
certificateDictionary() const477 CFDictionaryRef 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