1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Company 100 Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#import "config.h"
28#import "WebCoreArgumentCoders.h"
29
30#import "ArgumentCodersCF.h"
31#import "DataReference.h"
32#import "WebKitSystemInterface.h"
33#import <WebCore/CertificateInfo.h>
34#import <WebCore/ContentFilter.h>
35#import <WebCore/KeyboardEvent.h>
36#import <WebCore/ProtectionSpace.h>
37#import <WebCore/ResourceError.h>
38#import <WebCore/ResourceRequest.h>
39
40#if USE(CFNETWORK)
41#import <CFNetwork/CFURLRequest.h>
42#endif
43
44using namespace WebCore;
45
46namespace IPC {
47
48#if USE(CFNETWORK)
49void ArgumentCoder<ResourceRequest>::encodePlatformData(ArgumentEncoder& encoder, const ResourceRequest& resourceRequest)
50{
51    RetainPtr<CFURLRequestRef> requestToSerialize = resourceRequest.cfURLRequest(DoNotUpdateHTTPBody);
52
53    bool requestIsPresent = requestToSerialize;
54    encoder << requestIsPresent;
55
56    if (!requestIsPresent)
57        return;
58
59    // We don't send HTTP body over IPC for better performance.
60    // Also, it's not always possible to do, as streams can only be created in process that does networking.
61    RetainPtr<CFDataRef> requestHTTPBody = adoptCF(CFURLRequestCopyHTTPRequestBody(requestToSerialize.get()));
62    RetainPtr<CFReadStreamRef> requestHTTPBodyStream = adoptCF(CFURLRequestCopyHTTPRequestBodyStream(requestToSerialize.get()));
63    if (requestHTTPBody || requestHTTPBodyStream) {
64        CFMutableURLRequestRef mutableRequest = CFURLRequestCreateMutableCopy(0, requestToSerialize.get());
65        requestToSerialize = adoptCF(mutableRequest);
66        CFURLRequestSetHTTPRequestBody(mutableRequest, nil);
67        CFURLRequestSetHTTPRequestBodyStream(mutableRequest, nil);
68    }
69
70    RetainPtr<CFDictionaryRef> dictionary = adoptCF(WKCFURLRequestCreateSerializableRepresentation(requestToSerialize.get(), IPC::tokenNullTypeRef()));
71    IPC::encode(encoder, dictionary.get());
72
73    // The fallback array is part of CFURLRequest, but it is not encoded by WKCFURLRequestCreateSerializableRepresentation.
74    encoder << resourceRequest.responseContentDispositionEncodingFallbackArray();
75}
76#else
77void ArgumentCoder<ResourceRequest>::encodePlatformData(ArgumentEncoder& encoder, const ResourceRequest& resourceRequest)
78{
79    RetainPtr<NSURLRequest> requestToSerialize = resourceRequest.nsURLRequest(DoNotUpdateHTTPBody);
80
81    bool requestIsPresent = requestToSerialize;
82    encoder << requestIsPresent;
83
84    if (!requestIsPresent)
85        return;
86
87    // We don't send HTTP body over IPC for better performance.
88    // Also, it's not always possible to do, as streams can only be created in process that does networking.
89    if ([requestToSerialize HTTPBody] || [requestToSerialize HTTPBodyStream]) {
90        requestToSerialize = adoptNS([requestToSerialize mutableCopy]);
91        [(NSMutableURLRequest *)requestToSerialize setHTTPBody:nil];
92        [(NSMutableURLRequest *)requestToSerialize setHTTPBodyStream:nil];
93    }
94
95    RetainPtr<CFDictionaryRef> dictionary = adoptCF(WKNSURLRequestCreateSerializableRepresentation(requestToSerialize.get(), IPC::tokenNullTypeRef()));
96    IPC::encode(encoder, dictionary.get());
97
98    // The fallback array is part of NSURLRequest, but it is not encoded by WKNSURLRequestCreateSerializableRepresentation.
99    encoder << resourceRequest.responseContentDispositionEncodingFallbackArray();
100}
101#endif
102
103bool ArgumentCoder<ResourceRequest>::decodePlatformData(ArgumentDecoder& decoder, ResourceRequest& resourceRequest)
104{
105    bool requestIsPresent;
106    if (!decoder.decode(requestIsPresent))
107        return false;
108
109    if (!requestIsPresent) {
110        resourceRequest = ResourceRequest();
111        return true;
112    }
113
114    RetainPtr<CFDictionaryRef> dictionary;
115    if (!IPC::decode(decoder, dictionary))
116        return false;
117
118#if USE(CFNETWORK)
119    RetainPtr<CFURLRequestRef> cfURLRequest = adoptCF(WKCreateCFURLRequestFromSerializableRepresentation(dictionary.get(), IPC::tokenNullTypeRef()));
120    if (!cfURLRequest)
121        return false;
122
123    resourceRequest = ResourceRequest(cfURLRequest.get());
124#else
125    RetainPtr<NSURLRequest> nsURLRequest = WKNSURLRequestFromSerializableRepresentation(dictionary.get(), IPC::tokenNullTypeRef());
126    if (!nsURLRequest)
127        return false;
128
129    resourceRequest = ResourceRequest(nsURLRequest.get());
130#endif
131
132    Vector<String> responseContentDispositionEncodingFallbackArray;
133    if (!decoder.decode(responseContentDispositionEncodingFallbackArray))
134        return false;
135
136    resourceRequest.setResponseContentDispositionEncodingFallbackArray(
137        responseContentDispositionEncodingFallbackArray.size() > 0 ? responseContentDispositionEncodingFallbackArray[0] : String(),
138        responseContentDispositionEncodingFallbackArray.size() > 1 ? responseContentDispositionEncodingFallbackArray[1] : String(),
139        responseContentDispositionEncodingFallbackArray.size() > 2 ? responseContentDispositionEncodingFallbackArray[2] : String()
140    );
141
142    return true;
143}
144
145void ArgumentCoder<ResourceResponse>::encodePlatformData(ArgumentEncoder& encoder, const ResourceResponse& resourceResponse)
146{
147    bool responseIsPresent = resourceResponse.platformResponseIsUpToDate() && resourceResponse.nsURLResponse();
148    encoder << responseIsPresent;
149
150    if (!responseIsPresent)
151        return;
152
153    RetainPtr<CFDictionaryRef> dictionary = adoptCF(WKNSURLResponseCreateSerializableRepresentation(resourceResponse.nsURLResponse(), IPC::tokenNullTypeRef()));
154    IPC::encode(encoder, dictionary.get());
155}
156
157bool ArgumentCoder<ResourceResponse>::decodePlatformData(ArgumentDecoder& decoder, ResourceResponse& resourceResponse)
158{
159    bool responseIsPresent;
160    if (!decoder.decode(responseIsPresent))
161        return false;
162
163    if (!responseIsPresent) {
164        resourceResponse = ResourceResponse();
165        return true;
166    }
167
168    RetainPtr<CFDictionaryRef> dictionary;
169    if (!IPC::decode(decoder, dictionary))
170        return false;
171
172    RetainPtr<NSURLResponse> nsURLResponse = WKNSURLResponseFromSerializableRepresentation(dictionary.get(), IPC::tokenNullTypeRef());
173
174    if (!nsURLResponse)
175        return false;
176
177    resourceResponse = ResourceResponse(nsURLResponse.get());
178    return true;
179}
180
181void ArgumentCoder<CertificateInfo>::encode(ArgumentEncoder& encoder, const CertificateInfo& certificateInfo)
182{
183    CFArrayRef certificateChain = certificateInfo.certificateChain();
184    if (!certificateChain) {
185        encoder << false;
186        return;
187    }
188
189    encoder << true;
190    IPC::encode(encoder, certificateChain);
191}
192
193bool ArgumentCoder<CertificateInfo>::decode(ArgumentDecoder& decoder, CertificateInfo& certificateInfo)
194{
195    bool hasCertificateChain;
196    if (!decoder.decode(hasCertificateChain))
197        return false;
198
199    if (!hasCertificateChain)
200        return true;
201
202    RetainPtr<CFArrayRef> certificateChain;
203    if (!IPC::decode(decoder, certificateChain))
204        return false;
205
206    certificateInfo.setCertificateChain(certificateChain.get());
207
208    return true;
209}
210
211void ArgumentCoder<ResourceError>::encodePlatformData(ArgumentEncoder& encoder, const ResourceError& resourceError)
212{
213    bool errorIsNull = resourceError.isNull();
214    encoder << errorIsNull;
215
216    if (errorIsNull)
217        return;
218
219    NSError *nsError = resourceError.nsError();
220
221    String domain = [nsError domain];
222    encoder << domain;
223
224    int64_t code = [nsError code];
225    encoder << code;
226
227    NSDictionary *userInfo = [nsError userInfo];
228
229    RetainPtr<CFMutableDictionaryRef> filteredUserInfo = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, userInfo.count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
230
231    [userInfo enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL*) {
232        if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSURL class]])
233            CFDictionarySetValue(filteredUserInfo.get(), key, value);
234    }];
235
236    IPC::encode(encoder, filteredUserInfo.get());
237
238    id peerCertificateChain = [userInfo objectForKey:@"NSErrorPeerCertificateChainKey"];
239    if (!peerCertificateChain) {
240        if (SecTrustRef peerTrust = (SecTrustRef)[userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey]) {
241            CFIndex count = SecTrustGetCertificateCount(peerTrust);
242            peerCertificateChain = [NSMutableArray arrayWithCapacity:count];
243            for (CFIndex i = 0; i < count; ++i)
244                [peerCertificateChain addObject:(id)SecTrustGetCertificateAtIndex(peerTrust, i)];
245        }
246    }
247    ASSERT(!peerCertificateChain || [peerCertificateChain isKindOfClass:[NSArray class]]);
248    encoder << CertificateInfo((CFArrayRef)peerCertificateChain);
249}
250
251bool ArgumentCoder<ResourceError>::decodePlatformData(ArgumentDecoder& decoder, ResourceError& resourceError)
252{
253    bool errorIsNull;
254    if (!decoder.decode(errorIsNull))
255        return false;
256
257    if (errorIsNull) {
258        resourceError = ResourceError();
259        return true;
260    }
261
262    String domain;
263    if (!decoder.decode(domain))
264        return false;
265
266    int64_t code;
267    if (!decoder.decode(code))
268        return false;
269
270    RetainPtr<CFDictionaryRef> userInfo;
271    if (!IPC::decode(decoder, userInfo))
272        return false;
273
274    CertificateInfo certificate;
275    if (!decoder.decode(certificate))
276        return false;
277
278    if (certificate.certificateChain()) {
279        userInfo = adoptCF(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(userInfo.get()) + 1, userInfo.get()));
280        CFDictionarySetValue((CFMutableDictionaryRef)userInfo.get(), CFSTR("NSErrorPeerCertificateChainKey"), (CFArrayRef)certificate.certificateChain());
281    }
282
283    RetainPtr<NSError> nsError = adoptNS([[NSError alloc] initWithDomain:domain code:code userInfo:(NSDictionary *)userInfo.get()]);
284
285    resourceError = ResourceError(nsError.get());
286    return true;
287}
288
289void ArgumentCoder<ProtectionSpace>::encodePlatformData(ArgumentEncoder& encoder, const ProtectionSpace& space)
290{
291    RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
292    RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
293    [archiver setRequiresSecureCoding:YES];
294    [archiver encodeObject:space.nsSpace() forKey:@"protectionSpace"];
295    [archiver finishEncoding];
296    IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
297}
298
299bool ArgumentCoder<ProtectionSpace>::decodePlatformData(ArgumentDecoder& decoder, ProtectionSpace& space)
300{
301    RetainPtr<CFDataRef> data;
302    if (!IPC::decode(decoder, data))
303        return false;
304
305    RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
306    [unarchiver setRequiresSecureCoding:YES];
307    @try {
308        if (RetainPtr<NSURLProtectionSpace> nsSpace = [unarchiver decodeObjectOfClass:[NSURLProtectionSpace class] forKey:@"protectionSpace"])
309            space = ProtectionSpace(nsSpace.get());
310    } @catch (NSException *exception) {
311        LOG_ERROR("Failed to decode NSURLProtectionSpace: %@", exception);
312    }
313
314    [unarchiver finishDecoding];
315    return true;
316}
317
318void ArgumentCoder<KeypressCommand>::encode(ArgumentEncoder& encoder, const KeypressCommand& keypressCommand)
319{
320    encoder << keypressCommand.commandName << keypressCommand.text;
321}
322
323bool ArgumentCoder<KeypressCommand>::decode(ArgumentDecoder& decoder, KeypressCommand& keypressCommand)
324{
325    if (!decoder.decode(keypressCommand.commandName))
326        return false;
327
328    if (!decoder.decode(keypressCommand.text))
329        return false;
330
331    return true;
332}
333
334void ArgumentCoder<ContentFilter>::encode(ArgumentEncoder& encoder, const ContentFilter& contentFilter)
335{
336    RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
337    RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
338    [archiver setRequiresSecureCoding:YES];
339    contentFilter.encode(archiver.get());
340    [archiver finishEncoding];
341    IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
342}
343
344bool ArgumentCoder<ContentFilter>::decode(ArgumentDecoder& decoder, ContentFilter& contentFilter)
345{
346    RetainPtr<CFDataRef> data;
347    if (!IPC::decode(decoder, data))
348        return false;
349
350    RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
351    [unarchiver setRequiresSecureCoding:YES];
352    if (!ContentFilter::decode(unarchiver.get(), contentFilter))
353        return false;
354
355    [unarchiver finishDecoding];
356    return true;
357}
358
359} // namespace IPC
360