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