1/*
2 * Copyright (C) 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 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 "config.h"
27#include "ResourceError.h"
28
29#if USE(CFNETWORK)
30
31#include "URL.h"
32#include <CoreFoundation/CFError.h>
33#include <CFNetwork/CFNetworkErrors.h>
34#include <wtf/RetainPtr.h>
35
36#if PLATFORM(WIN)
37#include <WebKitSystemInterface/WebKitSystemInterface.h>
38#endif
39
40namespace WebCore {
41
42ResourceError::ResourceError(CFErrorRef cfError)
43    : m_dataIsUpToDate(false)
44    , m_platformError(cfError)
45{
46    m_isNull = !cfError;
47    if (!m_isNull)
48        m_isTimeout = CFErrorGetCode(m_platformError.get()) == kCFURLErrorTimedOut;
49}
50
51#if PLATFORM(WIN)
52ResourceError::ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription, CFDataRef certificate)
53    : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription)
54    , m_dataIsUpToDate(true)
55    , m_certificate(certificate)
56{
57}
58
59PCCERT_CONTEXT ResourceError::certificate() const
60{
61    if (!m_certificate)
62        return 0;
63
64    return reinterpret_cast<PCCERT_CONTEXT>(CFDataGetBytePtr(m_certificate.get()));
65}
66
67void ResourceError::setCertificate(CFDataRef certificate)
68{
69    m_certificate = certificate;
70}
71#endif // PLATFORM(WIN)
72
73const CFStringRef failingURLStringKey = CFSTR("NSErrorFailingURLStringKey");
74const CFStringRef failingURLKey = CFSTR("NSErrorFailingURLKey");
75
76void ResourceError::platformLazyInit()
77{
78    if (m_dataIsUpToDate)
79        return;
80
81    if (!m_platformError)
82        return;
83
84    CFStringRef domain = CFErrorGetDomain(m_platformError.get());
85    if (domain == kCFErrorDomainMach || domain == kCFErrorDomainCocoa)
86        m_domain ="NSCustomErrorDomain";
87    else if (domain == kCFErrorDomainCFNetwork)
88        m_domain = "CFURLErrorDomain";
89    else if (domain == kCFErrorDomainPOSIX)
90        m_domain = "NSPOSIXErrorDomain";
91    else if (domain == kCFErrorDomainOSStatus)
92        m_domain = "NSOSStatusErrorDomain";
93    else if (domain == kCFErrorDomainWinSock)
94        m_domain = "kCFErrorDomainWinSock";
95    else
96        m_domain = domain;
97
98    m_errorCode = CFErrorGetCode(m_platformError.get());
99
100    RetainPtr<CFDictionaryRef> userInfo = adoptCF(CFErrorCopyUserInfo(m_platformError.get()));
101    if (userInfo.get()) {
102        CFStringRef failingURLString = (CFStringRef) CFDictionaryGetValue(userInfo.get(), failingURLStringKey);
103        if (failingURLString)
104            m_failingURL = String(failingURLString);
105        else {
106            CFURLRef failingURL = (CFURLRef) CFDictionaryGetValue(userInfo.get(), failingURLKey);
107            if (failingURL) {
108                RetainPtr<CFURLRef> absoluteURLRef = adoptCF(CFURLCopyAbsoluteURL(failingURL));
109                if (absoluteURLRef.get()) {
110                    // FIXME: CFURLGetString returns a normalized URL which is different from what is actually used by CFNetwork.
111                    // We should use CFURLGetBytes instead.
112                    failingURLString = CFURLGetString(absoluteURLRef.get());
113                    m_failingURL = String(failingURLString);
114                }
115            }
116        }
117        m_localizedDescription = (CFStringRef) CFDictionaryGetValue(userInfo.get(), kCFErrorLocalizedDescriptionKey);
118
119#if PLATFORM(WIN)
120        m_certificate = wkGetSSLPeerCertificateData(userInfo.get());
121#endif
122    }
123
124    m_dataIsUpToDate = true;
125}
126
127void ResourceError::platformCopy(ResourceError& errorCopy) const
128{
129#if PLATFORM(WIN)
130    errorCopy.m_certificate = m_certificate;
131#else
132    UNUSED_PARAM(errorCopy);
133#endif
134}
135
136bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& b)
137{
138    return a.cfError() == b.cfError();
139}
140
141CFErrorRef ResourceError::cfError() const
142{
143    if (m_isNull) {
144        ASSERT(!m_platformError);
145        return 0;
146    }
147
148    if (!m_platformError) {
149        RetainPtr<CFMutableDictionaryRef> userInfo = adoptCF(CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
150
151        if (!m_localizedDescription.isEmpty())
152            CFDictionarySetValue(userInfo.get(), kCFErrorLocalizedDescriptionKey, m_localizedDescription.createCFString().get());
153
154        if (!m_failingURL.isEmpty()) {
155            RetainPtr<CFStringRef> failingURLString = m_failingURL.createCFString();
156            CFDictionarySetValue(userInfo.get(), failingURLStringKey, failingURLString.get());
157            RetainPtr<CFURLRef> url = adoptCF(CFURLCreateWithString(0, failingURLString.get(), 0));
158            CFDictionarySetValue(userInfo.get(), failingURLKey, url.get());
159        }
160
161#if PLATFORM(WIN)
162        if (m_certificate)
163            wkSetSSLPeerCertificateData(userInfo.get(), m_certificate.get());
164#endif
165
166        m_platformError = adoptCF(CFErrorCreate(0, m_domain.createCFString().get(), m_errorCode, userInfo.get()));
167    }
168
169    return m_platformError.get();
170}
171
172ResourceError::operator CFErrorRef() const
173{
174    return cfError();
175}
176
177// FIXME: Once <rdar://problem/5050841> is fixed we can remove this constructor.
178ResourceError::ResourceError(CFStreamError error)
179    : m_dataIsUpToDate(true)
180{
181    m_isNull = false;
182    m_errorCode = error.error;
183
184    switch(error.domain) {
185    case kCFStreamErrorDomainCustom:
186        m_domain ="NSCustomErrorDomain";
187        break;
188    case kCFStreamErrorDomainPOSIX:
189        m_domain = "NSPOSIXErrorDomain";
190        break;
191    case kCFStreamErrorDomainMacOSStatus:
192        m_domain = "NSOSStatusErrorDomain";
193        break;
194    }
195}
196
197CFStreamError ResourceError::cfStreamError() const
198{
199    lazyInit();
200
201    CFStreamError result;
202    result.error = m_errorCode;
203
204    if (m_domain == "NSCustomErrorDomain")
205        result.domain = kCFStreamErrorDomainCustom;
206    else if (m_domain == "NSPOSIXErrorDomain")
207        result.domain = kCFStreamErrorDomainPOSIX;
208    else if (m_domain == "NSOSStatusErrorDomain")
209        result.domain = kCFStreamErrorDomainMacOSStatus;
210    else {
211        result.domain = kCFStreamErrorDomainCustom;
212        ASSERT_NOT_REACHED();
213    }
214
215    return result;
216}
217
218ResourceError::operator CFStreamError() const
219{
220    return cfStreamError();
221}
222
223} // namespace WebCore
224
225#endif // USE(CFNETWORK)
226