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