1/*
2 * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ArgumentCodersCF.h"
28
29#include "ArgumentDecoder.h"
30#include "ArgumentEncoder.h"
31#include "DataReference.h"
32#include <WebCore/CFURLExtras.h>
33#include <wtf/Vector.h>
34
35#if PLATFORM(MAC)
36#import <Foundation/Foundation.h>
37#endif
38
39using namespace WebCore;
40
41namespace CoreIPC {
42
43CFTypeRef tokenNullTypeRef()
44{
45    static CFStringRef tokenNullType = CFSTR("WKNull");
46    return tokenNullType;
47}
48
49enum CFType {
50    CFArray,
51    CFBoolean,
52    CFData,
53    CFDate,
54    CFDictionary,
55    CFNull,
56    CFNumber,
57    CFString,
58    CFURL,
59#if USE(SECURITY_FRAMEWORK)
60    SecCertificate,
61    SecKeychainItem,
62#endif
63    Null,
64    Unknown,
65};
66
67static CFType typeFromCFTypeRef(CFTypeRef type)
68{
69    ASSERT(type);
70
71    if (type == tokenNullTypeRef())
72        return Null;
73
74    CFTypeID typeID = CFGetTypeID(type);
75    if (typeID == CFArrayGetTypeID())
76        return CFArray;
77    if (typeID == CFBooleanGetTypeID())
78        return CFBoolean;
79    if (typeID == CFDataGetTypeID())
80        return CFData;
81    if (typeID == CFDateGetTypeID())
82        return CFDate;
83    if (typeID == CFDictionaryGetTypeID())
84        return CFDictionary;
85    if (typeID == CFNullGetTypeID())
86        return CFNull;
87    if (typeID == CFNumberGetTypeID())
88        return CFNumber;
89    if (typeID == CFStringGetTypeID())
90        return CFString;
91    if (typeID == CFURLGetTypeID())
92        return CFURL;
93#if USE(SECURITY_FRAMEWORK)
94    if (typeID == SecCertificateGetTypeID())
95        return SecCertificate;
96    if (typeID == SecKeychainItemGetTypeID())
97        return SecKeychainItem;
98#endif
99
100    ASSERT_NOT_REACHED();
101    return Unknown;
102}
103
104void encode(ArgumentEncoder& encoder, CFTypeRef typeRef)
105{
106    CFType type = typeFromCFTypeRef(typeRef);
107    encoder.encodeEnum(type);
108
109    switch (type) {
110    case CFArray:
111        encode(encoder, static_cast<CFArrayRef>(typeRef));
112        return;
113    case CFBoolean:
114        encode(encoder, static_cast<CFBooleanRef>(typeRef));
115        return;
116    case CFData:
117        encode(encoder, static_cast<CFDataRef>(typeRef));
118        return;
119    case CFDate:
120        encode(encoder, static_cast<CFDateRef>(typeRef));
121        return;
122    case CFDictionary:
123        encode(encoder, static_cast<CFDictionaryRef>(typeRef));
124        return;
125    case CFNull:
126        return;
127    case CFNumber:
128        encode(encoder, static_cast<CFNumberRef>(typeRef));
129        return;
130    case CFString:
131        encode(encoder, static_cast<CFStringRef>(typeRef));
132        return;
133    case CFURL:
134        encode(encoder, static_cast<CFURLRef>(typeRef));
135        return;
136#if USE(SECURITY_FRAMEWORK)
137    case SecCertificate:
138        encode(encoder, (SecCertificateRef)typeRef);
139        return;
140    case SecKeychainItem:
141        encode(encoder, (SecKeychainItemRef)typeRef);
142        return;
143#endif
144    case Null:
145        return;
146    case Unknown:
147        break;
148    }
149
150    ASSERT_NOT_REACHED();
151}
152
153bool decode(ArgumentDecoder& decoder, RetainPtr<CFTypeRef>& result)
154{
155    CFType type;
156    if (!decoder.decodeEnum(type))
157        return false;
158
159    switch (type) {
160    case CFArray: {
161        RetainPtr<CFArrayRef> array;
162        if (!decode(decoder, array))
163            return false;
164        result = adoptCF(array.leakRef());
165        return true;
166    }
167    case CFBoolean: {
168        RetainPtr<CFBooleanRef> boolean;
169        if (!decode(decoder, boolean))
170            return false;
171        result = adoptCF(boolean.leakRef());
172        return true;
173    }
174    case CFData: {
175        RetainPtr<CFDataRef> data;
176        if (!decode(decoder, data))
177            return false;
178        result = adoptCF(data.leakRef());
179        return true;
180    }
181    case CFDate: {
182        RetainPtr<CFDateRef> date;
183        if (!decode(decoder, date))
184            return false;
185        result = adoptCF(date.leakRef());
186        return true;
187    }
188    case CFDictionary: {
189        RetainPtr<CFDictionaryRef> dictionary;
190        if (!decode(decoder, dictionary))
191            return false;
192        result = adoptCF(dictionary.leakRef());
193        return true;
194    }
195    case CFNull:
196        result = adoptCF(kCFNull);
197        return true;
198    case CFNumber: {
199        RetainPtr<CFNumberRef> number;
200        if (!decode(decoder, number))
201            return false;
202        result = adoptCF(number.leakRef());
203        return true;
204    }
205    case CFString: {
206        RetainPtr<CFStringRef> string;
207        if (!decode(decoder, string))
208            return false;
209        result = adoptCF(string.leakRef());
210        return true;
211    }
212    case CFURL: {
213        RetainPtr<CFURLRef> url;
214        if (!decode(decoder, url))
215            return false;
216        result = adoptCF(url.leakRef());
217        return true;
218    }
219#if USE(SECURITY_FRAMEWORK)
220    case SecCertificate: {
221        RetainPtr<SecCertificateRef> certificate;
222        if (!decode(decoder, certificate))
223            return false;
224        result = adoptCF(certificate.leakRef());
225        return true;
226    }
227    case SecKeychainItem: {
228        RetainPtr<SecKeychainItemRef> keychainItem;
229        if (!decode(decoder, keychainItem))
230            return false;
231        result = adoptCF(keychainItem.leakRef());
232        return true;
233    }
234#endif
235    case Null:
236        result = tokenNullTypeRef();
237        return true;
238    case Unknown:
239        ASSERT_NOT_REACHED();
240        return false;
241    }
242
243    return false;
244}
245
246void encode(ArgumentEncoder& encoder, CFArrayRef array)
247{
248    CFIndex size = CFArrayGetCount(array);
249    Vector<CFTypeRef, 32> values(size);
250
251    CFArrayGetValues(array, CFRangeMake(0, size), values.data());
252
253    encoder << static_cast<uint64_t>(size);
254
255    for (CFIndex i = 0; i < size; ++i) {
256        ASSERT(values[i]);
257
258        encode(encoder, values[i]);
259    }
260}
261
262bool decode(ArgumentDecoder& decoder, RetainPtr<CFArrayRef>& result)
263{
264    uint64_t size;
265    if (!decoder.decode(size))
266        return false;
267
268    RetainPtr<CFMutableArrayRef> array = adoptCF(CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
269
270    for (size_t i = 0; i < size; ++i) {
271        RetainPtr<CFTypeRef> element;
272        if (!decode(decoder, element))
273            return false;
274
275        CFArrayAppendValue(array.get(), element.get());
276    }
277
278    result = adoptCF(array.leakRef());
279    return true;
280}
281
282void encode(ArgumentEncoder& encoder, CFBooleanRef boolean)
283{
284    encoder << static_cast<bool>(CFBooleanGetValue(boolean));
285}
286
287bool decode(ArgumentDecoder& decoder, RetainPtr<CFBooleanRef>& result)
288{
289    bool boolean;
290    if (!decoder.decode(boolean))
291        return false;
292
293    result = adoptCF(boolean ? kCFBooleanTrue : kCFBooleanFalse);
294    return true;
295}
296
297void encode(ArgumentEncoder& encoder, CFDataRef data)
298{
299    CFIndex length = CFDataGetLength(data);
300    const UInt8* bytePtr = CFDataGetBytePtr(data);
301
302    encoder << CoreIPC::DataReference(bytePtr, length);
303}
304
305bool decode(ArgumentDecoder& decoder, RetainPtr<CFDataRef>& result)
306{
307    CoreIPC::DataReference dataReference;
308    if (!decoder.decode(dataReference))
309        return false;
310
311    result = adoptCF(CFDataCreate(0, dataReference.data(), dataReference.size()));
312    return true;
313}
314
315void encode(ArgumentEncoder& encoder, CFDateRef date)
316{
317    encoder << static_cast<double>(CFDateGetAbsoluteTime(date));
318}
319
320bool decode(ArgumentDecoder& decoder, RetainPtr<CFDateRef>& result)
321{
322    double absoluteTime;
323    if (!decoder.decode(absoluteTime))
324        return false;
325
326    result = adoptCF(CFDateCreate(0, absoluteTime));
327    return true;
328}
329
330void encode(ArgumentEncoder& encoder, CFDictionaryRef dictionary)
331{
332    CFIndex size = CFDictionaryGetCount(dictionary);
333    Vector<CFTypeRef, 32> keys(size);
334    Vector<CFTypeRef, 32> values(size);
335
336    CFDictionaryGetKeysAndValues(dictionary, keys.data(), values.data());
337
338    encoder << static_cast<uint64_t>(size);
339
340    for (CFIndex i = 0; i < size; ++i) {
341        ASSERT(keys[i]);
342        ASSERT(CFGetTypeID(keys[i]) == CFStringGetTypeID());
343        ASSERT(values[i]);
344
345        // Ignore values we don't recognize.
346        if (typeFromCFTypeRef(values[i]) == Unknown)
347            continue;
348
349        encode(encoder, static_cast<CFStringRef>(keys[i]));
350        encode(encoder, values[i]);
351    }
352}
353
354bool decode(ArgumentDecoder& decoder, RetainPtr<CFDictionaryRef>& result)
355{
356    uint64_t size;
357    if (!decoder.decode(size))
358        return false;
359
360    RetainPtr<CFMutableDictionaryRef> dictionary = adoptCF(CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
361    for (uint64_t i = 0; i < size; ++i) {
362        // Try to decode the key name.
363        RetainPtr<CFStringRef> key;
364        if (!decode(decoder, key))
365            return false;
366
367        RetainPtr<CFTypeRef> value;
368        if (!decode(decoder, value))
369            return false;
370
371        CFDictionarySetValue(dictionary.get(), key.get(), value.get());
372    }
373
374    result = adoptCF(dictionary.leakRef());
375    return true;
376}
377
378void encode(ArgumentEncoder& encoder, CFNumberRef number)
379{
380    CFNumberType numberType = CFNumberGetType(number);
381
382    Vector<uint8_t> buffer(CFNumberGetByteSize(number));
383    bool result = CFNumberGetValue(number, numberType, buffer.data());
384    ASSERT_UNUSED(result, result);
385
386    encoder.encodeEnum(numberType);
387    encoder << CoreIPC::DataReference(buffer);
388}
389
390static size_t sizeForNumberType(CFNumberType numberType)
391{
392    switch (numberType) {
393    case kCFNumberSInt8Type:
394        return sizeof(SInt8);
395    case kCFNumberSInt16Type:
396        return sizeof(SInt16);
397    case kCFNumberSInt32Type:
398        return sizeof(SInt32);
399    case kCFNumberSInt64Type:
400        return sizeof(SInt64);
401    case kCFNumberFloat32Type:
402        return sizeof(Float32);
403    case kCFNumberFloat64Type:
404        return sizeof(Float64);
405    case kCFNumberCharType:
406        return sizeof(char);
407    case kCFNumberShortType:
408        return sizeof(short);
409    case kCFNumberIntType:
410        return sizeof(int);
411    case kCFNumberLongType:
412        return sizeof(long);
413    case kCFNumberLongLongType:
414        return sizeof(long long);
415    case kCFNumberFloatType:
416        return sizeof(float);
417    case kCFNumberDoubleType:
418        return sizeof(double);
419    case kCFNumberCFIndexType:
420        return sizeof(CFIndex);
421    case kCFNumberNSIntegerType:
422#ifdef __LP64__
423        return sizeof(long);
424#else
425        return sizeof(int);
426#endif
427    case kCFNumberCGFloatType:
428#ifdef __LP64__
429        return sizeof(double);
430#else
431        return sizeof(float);
432#endif
433    }
434
435    return 0;
436}
437
438bool decode(ArgumentDecoder& decoder, RetainPtr<CFNumberRef>& result)
439{
440    CFNumberType numberType;
441    if (!decoder.decodeEnum(numberType))
442        return false;
443
444    CoreIPC::DataReference dataReference;
445    if (!decoder.decode(dataReference))
446        return false;
447
448    size_t neededBufferSize = sizeForNumberType(numberType);
449    if (!neededBufferSize || dataReference.size() != neededBufferSize)
450        return false;
451
452    ASSERT(dataReference.data());
453    CFNumberRef number = CFNumberCreate(0, numberType, dataReference.data());
454    result = adoptCF(number);
455
456    return true;
457}
458
459void encode(ArgumentEncoder& encoder, CFStringRef string)
460{
461    CFIndex length = CFStringGetLength(string);
462    CFStringEncoding encoding = CFStringGetFastestEncoding(string);
463
464    CFRange range = CFRangeMake(0, length);
465    CFIndex bufferLength = 0;
466
467    CFIndex numConvertedBytes = CFStringGetBytes(string, range, encoding, 0, false, 0, 0, &bufferLength);
468    ASSERT(numConvertedBytes == length);
469
470    Vector<UInt8, 128> buffer(bufferLength);
471    numConvertedBytes = CFStringGetBytes(string, range, encoding, 0, false, buffer.data(), buffer.size(), &bufferLength);
472    ASSERT(numConvertedBytes == length);
473
474    encoder.encodeEnum(encoding);
475    encoder << CoreIPC::DataReference(buffer);
476}
477
478bool decode(ArgumentDecoder& decoder, RetainPtr<CFStringRef>& result)
479{
480    CFStringEncoding encoding;
481    if (!decoder.decodeEnum(encoding))
482        return false;
483
484    if (!CFStringIsEncodingAvailable(encoding))
485        return false;
486
487    CoreIPC::DataReference dataReference;
488    if (!decoder.decode(dataReference))
489        return false;
490
491    CFStringRef string = CFStringCreateWithBytes(0, dataReference.data(), dataReference.size(), encoding, false);
492    if (!string)
493        return false;
494
495    result = adoptCF(string);
496    return true;
497}
498
499void encode(ArgumentEncoder& encoder, CFURLRef url)
500{
501    CFURLRef baseURL = CFURLGetBaseURL(url);
502    encoder << static_cast<bool>(baseURL);
503    if (baseURL)
504        encode(encoder, baseURL);
505
506    URLCharBuffer urlBytes;
507    getURLBytes(url, urlBytes);
508    CoreIPC::DataReference dataReference(reinterpret_cast<const uint8_t*>(urlBytes.data()), urlBytes.size());
509    encoder << dataReference;
510}
511
512bool decode(ArgumentDecoder& decoder, RetainPtr<CFURLRef>& result)
513{
514    RetainPtr<CFURLRef> baseURL;
515    bool hasBaseURL;
516    if (!decoder.decode(hasBaseURL))
517        return false;
518    if (hasBaseURL) {
519        if (!decode(decoder, baseURL))
520            return false;
521    }
522
523    CoreIPC::DataReference urlBytes;
524    if (!decoder.decode(urlBytes))
525        return false;
526
527#if PLATFORM(MAC)
528    // FIXME: Move this to ArgumentCodersCFMac.mm and change this file back to be C++
529    // instead of Objective-C++.
530    if (urlBytes.isEmpty()) {
531        // CFURL can't hold an empty URL, unlike NSURL.
532        // FIXME: This discards base URL, which seems incorrect.
533        result = reinterpret_cast<CFURLRef>([NSURL URLWithString:@""]);
534        return true;
535    }
536#endif
537
538    result = createCFURLFromBuffer(reinterpret_cast<const char*>(urlBytes.data()), urlBytes.size(), baseURL.get());
539    return result;
540}
541
542#if USE(SECURITY_FRAMEWORK)
543void encode(ArgumentEncoder& encoder, SecCertificateRef certificate)
544{
545    RetainPtr<CFDataRef> data = adoptCF(SecCertificateCopyData(certificate));
546    encode(encoder, data.get());
547}
548
549bool decode(ArgumentDecoder& decoder, RetainPtr<SecCertificateRef>& result)
550{
551    RetainPtr<CFDataRef> data;
552    if (!decode(decoder, data))
553        return false;
554
555    result = adoptCF(SecCertificateCreateWithData(0, data.get()));
556    return true;
557}
558
559void encode(ArgumentEncoder& encoder, SecKeychainItemRef keychainItem)
560{
561    CFDataRef data;
562    if (SecKeychainItemCreatePersistentReference(keychainItem, &data) == errSecSuccess) {
563        encode(encoder, data);
564        CFRelease(data);
565    }
566}
567
568bool decode(ArgumentDecoder& decoder, RetainPtr<SecKeychainItemRef>& result)
569{
570    RetainPtr<CFDataRef> data;
571    if (!CoreIPC::decode(decoder, data))
572        return false;
573
574    SecKeychainItemRef item;
575    if (SecKeychainItemCopyFromPersistentReference(data.get(), &item) != errSecSuccess || !item)
576        return false;
577
578    result = adoptCF(item);
579    return true;
580}
581#endif
582
583} // namespace CoreIPC
584