/* * Copyright (C) 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ArgumentCodersCF.h" #include "ArgumentDecoder.h" #include "ArgumentEncoder.h" #include "DataReference.h" #include <wtf/Vector.h> namespace CoreIPC { CFTypeRef tokenNullTypeRef() { static CFStringRef tokenNullType = CFSTR("WKNull"); return tokenNullType; } enum CFType { CFArray, CFBoolean, CFData, CFDictionary, CFNull, CFNumber, CFString, CFURL, #if PLATFORM(MAC) SecCertificate, #endif Null, Unknown, }; static CFType typeFromCFTypeRef(CFTypeRef type) { ASSERT(type); if (type == tokenNullTypeRef()) return Null; CFTypeID typeID = CFGetTypeID(type); if (typeID == CFArrayGetTypeID()) return CFArray; if (typeID == CFBooleanGetTypeID()) return CFBoolean; if (typeID == CFDataGetTypeID()) return CFData; if (typeID == CFDictionaryGetTypeID()) return CFDictionary; if (typeID == CFNullGetTypeID()) return CFNull; if (typeID == CFNumberGetTypeID()) return CFNumber; if (typeID == CFStringGetTypeID()) return CFString; if (typeID == CFURLGetTypeID()) return CFURL; #if PLATFORM(MAC) if (typeID == SecCertificateGetTypeID()) return SecCertificate; #endif ASSERT_NOT_REACHED(); return Unknown; } static void encode(ArgumentEncoder* encoder, CFTypeRef typeRef) { CFType type = typeFromCFTypeRef(typeRef); encoder->encodeEnum(type); switch (type) { case CFArray: encode(encoder, static_cast<CFArrayRef>(typeRef)); return; case CFBoolean: encode(encoder, static_cast<CFBooleanRef>(typeRef)); return; case CFData: encode(encoder, static_cast<CFDataRef>(typeRef)); return; case CFDictionary: encode(encoder, static_cast<CFDictionaryRef>(typeRef)); return; case CFNull: return; case CFNumber: encode(encoder, static_cast<CFNumberRef>(typeRef)); return; case CFString: encode(encoder, static_cast<CFStringRef>(typeRef)); return; case CFURL: encode(encoder, static_cast<CFURLRef>(typeRef)); return; #if PLATFORM(MAC) case SecCertificate: encode(encoder, (SecCertificateRef)typeRef); return; #endif case Null: return; case Unknown: break; } ASSERT_NOT_REACHED(); } static bool decode(ArgumentDecoder* decoder, RetainPtr<CFTypeRef>& result) { CFType type; if (!decoder->decodeEnum(type)) return false; switch (type) { case CFArray: { RetainPtr<CFArrayRef> array; if (!decode(decoder, array)) return false; result.adoptCF(array.leakRef()); return true; } case CFBoolean: { RetainPtr<CFBooleanRef> boolean; if (!decode(decoder, boolean)) return false; result.adoptCF(boolean.leakRef()); return true; } case CFData: { RetainPtr<CFDataRef> data; if (!decode(decoder, data)) return false; result.adoptCF(data.leakRef()); return true; } case CFDictionary: { RetainPtr<CFDictionaryRef> dictionary; if (!decode(decoder, dictionary)) return false; result.adoptCF(dictionary.leakRef()); return true; } case CFNull: result.adoptCF(kCFNull); return true; case CFNumber: { RetainPtr<CFNumberRef> number; if (!decode(decoder, number)) return false; result.adoptCF(number.leakRef()); return true; } case CFString: { RetainPtr<CFStringRef> string; if (!decode(decoder, string)) return false; result.adoptCF(string.leakRef()); return true; } case CFURL: { RetainPtr<CFURLRef> url; if (!decode(decoder, url)) return false; result.adoptCF(url.leakRef()); return true; } #if PLATFORM(MAC) case SecCertificate: { RetainPtr<SecCertificateRef> certificate; if (!decode(decoder, certificate)) return false; result.adoptCF(certificate.leakRef()); return true; } #endif case Null: result = tokenNullTypeRef(); return true; case Unknown: ASSERT_NOT_REACHED(); return false; } return false; } void encode(ArgumentEncoder* encoder, CFArrayRef array) { CFIndex size = CFArrayGetCount(array); Vector<CFTypeRef, 32> values(size); CFArrayGetValues(array, CFRangeMake(0, size), values.data()); encoder->encodeUInt64(size); for (CFIndex i = 0; i < size; ++i) { ASSERT(values[i]); encode(encoder, values[i]); } } bool decode(ArgumentDecoder* decoder, RetainPtr<CFArrayRef>& result) { uint64_t size; if (!decoder->decodeUInt64(size)) return false; RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); for (size_t i = 0; i < size; ++i) { RetainPtr<CFTypeRef> element; if (!decode(decoder, element)) return false; CFArrayAppendValue(array.get(), element.get()); } result.adoptCF(array.leakRef()); return true; } void encode(ArgumentEncoder* encoder, CFBooleanRef boolean) { encoder->encodeBool(CFBooleanGetValue(boolean)); } bool decode(ArgumentDecoder* decoder, RetainPtr<CFBooleanRef>& result) { bool boolean; if (!decoder->decode(boolean)) return false; result.adoptCF(boolean ? kCFBooleanTrue : kCFBooleanFalse); return true; } void encode(ArgumentEncoder* encoder, CFDataRef data) { CFIndex length = CFDataGetLength(data); const UInt8* bytePtr = CFDataGetBytePtr(data); encoder->encodeBytes(bytePtr, length); } bool decode(ArgumentDecoder* decoder, RetainPtr<CFDataRef>& result) { CoreIPC::DataReference dataReference; if (!decoder->decode(dataReference)) return false; result.adoptCF(CFDataCreate(0, dataReference.data(), dataReference.size())); return true; } void encode(ArgumentEncoder* encoder, CFDictionaryRef dictionary) { CFIndex size = CFDictionaryGetCount(dictionary); Vector<CFTypeRef, 32> keys(size); Vector<CFTypeRef, 32> values(size); CFDictionaryGetKeysAndValues(dictionary, keys.data(), values.data()); encoder->encodeUInt64(size); for (CFIndex i = 0; i < size; ++i) { ASSERT(keys[i]); ASSERT(CFGetTypeID(keys[i]) == CFStringGetTypeID()); ASSERT(values[i]); // Ignore values we don't recognize. if (typeFromCFTypeRef(values[i]) == Unknown) continue; encode(encoder, static_cast<CFStringRef>(keys[i])); encode(encoder, values[i]); } } bool decode(ArgumentDecoder* decoder, RetainPtr<CFDictionaryRef>& result) { uint64_t size; if (!decoder->decodeUInt64(size)) return false; RetainPtr<CFMutableDictionaryRef> dictionary(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); for (uint64_t i = 0; i < size; ++i) { // Try to decode the key name. RetainPtr<CFStringRef> key; if (!decode(decoder, key)) return false; RetainPtr<CFTypeRef> value; if (!decode(decoder, value)) return false; CFDictionarySetValue(dictionary.get(), key.get(), value.get()); } result.adoptCF(dictionary.releaseRef()); return true; } void encode(ArgumentEncoder* encoder, CFNumberRef number) { CFNumberType numberType = CFNumberGetType(number); Vector<uint8_t> buffer(CFNumberGetByteSize(number)); bool result = CFNumberGetValue(number, numberType, buffer.data()); ASSERT_UNUSED(result, result); encoder->encodeEnum(numberType); encoder->encodeBytes(buffer.data(), buffer.size()); } static size_t sizeForNumberType(CFNumberType numberType) { switch (numberType) { case kCFNumberSInt8Type: return sizeof(SInt8); case kCFNumberSInt16Type: return sizeof(SInt16); case kCFNumberSInt32Type: return sizeof(SInt32); case kCFNumberSInt64Type: return sizeof(SInt64); case kCFNumberFloat32Type: return sizeof(Float32); case kCFNumberFloat64Type: return sizeof(Float64); case kCFNumberCharType: return sizeof(char); case kCFNumberShortType: return sizeof(short); case kCFNumberIntType: return sizeof(int); case kCFNumberLongType: return sizeof(long); case kCFNumberLongLongType: return sizeof(long long); case kCFNumberFloatType: return sizeof(float); case kCFNumberDoubleType: return sizeof(double); case kCFNumberCFIndexType: return sizeof(CFIndex); case kCFNumberNSIntegerType: #ifdef __LP64__ return sizeof(long); #else return sizeof(int); #endif case kCFNumberCGFloatType: #ifdef __LP64__ return sizeof(double); #else return sizeof(float); #endif } return 0; } bool decode(ArgumentDecoder* decoder, RetainPtr<CFNumberRef>& result) { CFNumberType numberType; if (!decoder->decodeEnum(numberType)) return false; CoreIPC::DataReference dataReference; if (!decoder->decode(dataReference)) return false; size_t neededBufferSize = sizeForNumberType(numberType); if (!neededBufferSize || dataReference.size() != neededBufferSize) return false; ASSERT(dataReference.data()); CFNumberRef number = CFNumberCreate(0, numberType, dataReference.data()); result.adoptCF(number); return true; } void encode(ArgumentEncoder* encoder, CFStringRef string) { CFIndex length = CFStringGetLength(string); CFStringEncoding encoding = CFStringGetFastestEncoding(string); CFRange range = CFRangeMake(0, length); CFIndex bufferLength = 0; CFIndex numConvertedBytes = CFStringGetBytes(string, range, encoding, 0, false, 0, 0, &bufferLength); ASSERT(numConvertedBytes == length); Vector<UInt8, 128> buffer(bufferLength); numConvertedBytes = CFStringGetBytes(string, range, encoding, 0, false, buffer.data(), buffer.size(), &bufferLength); ASSERT(numConvertedBytes == length); encoder->encodeEnum(encoding); encoder->encodeBytes(buffer.data(), bufferLength); } bool decode(ArgumentDecoder* decoder, RetainPtr<CFStringRef>& result) { CFStringEncoding encoding; if (!decoder->decodeEnum(encoding)) return false; if (!CFStringIsEncodingAvailable(encoding)) return false; CoreIPC::DataReference dataReference; if (!decoder->decode(dataReference)) return false; CFStringRef string = CFStringCreateWithBytes(0, dataReference.data(), dataReference.size(), encoding, false); if (!string) return false; result.adoptCF(string); return true; } void encode(ArgumentEncoder* encoder, CFURLRef url) { CFURLRef baseURL = CFURLGetBaseURL(url); encoder->encodeBool(baseURL); if (baseURL) encode(encoder, baseURL); encode(encoder, CFURLGetString(url)); } bool decode(ArgumentDecoder* decoder, RetainPtr<CFURLRef>& result) { RetainPtr<CFURLRef> baseURL; bool hasBaseURL; if (!decoder->decodeBool(hasBaseURL)) return false; if (hasBaseURL) { if (!decode(decoder, baseURL)) return false; } RetainPtr<CFStringRef> string; if (!decode(decoder, string)) return false; CFURLRef url = CFURLCreateWithString(0, string.get(), baseURL.get()); if (!url) return false; result.adoptCF(url); return true; } #if PLATFORM(MAC) void encode(ArgumentEncoder* encoder, SecCertificateRef certificate) { RetainPtr<CFDataRef> data(AdoptCF, SecCertificateCopyData(certificate)); encode(encoder, data.get()); } bool decode(ArgumentDecoder* decoder, RetainPtr<SecCertificateRef>& result) { RetainPtr<CFDataRef> data; if (!decode(decoder, data)) return false; result.adoptCF(SecCertificateCreateWithData(0, data.get())); return true; } #endif } // namespace CoreIPC