/* * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ // //CoreFoundation related utilities // #ifndef _H_CFUTILITIES #define _H_CFUTILITIES #include #include #include #include #include #undef check namespace Security { // // Traits of popular CF types // template struct CFTraits { }; template <> struct CFTraits { static bool check(CFTypeRef ref) { return true; } }; #define __SEC_CFTYPE(name) \ template <> struct CFTraits { \ static CFTypeID cfid() { return name##GetTypeID(); } \ static bool check(CFTypeRef ref) { return CFGetTypeID(ref) == cfid(); } \ }; __SEC_CFTYPE(CFNull) __SEC_CFTYPE(CFBoolean) __SEC_CFTYPE(CFNumber) __SEC_CFTYPE(CFString) __SEC_CFTYPE(CFData) __SEC_CFTYPE(CFDate) __SEC_CFTYPE(CFURL) __SEC_CFTYPE(CFBundle) __SEC_CFTYPE(CFArray) __SEC_CFTYPE(CFDictionary) __SEC_CFTYPE(CFSet) // // Initialize-only self-releasing CF object handler (lightweight). // template class CFRef { public: CFRef() : mRef(NULL) { } CFRef(CFType ref) : mRef(ref) { } ~CFRef() { this->release(); } CFRef(const CFRef &ref) : mRef(ref) {} template CFRef(const CFRef<_T> &ref) : mRef(ref) {} CFRef(CFTypeRef ref, OSStatus err) : mRef(CFType(ref)) { if (ref && !CFTraits::check(ref)) MacOSError::throwMe(err); } CFRef &take(CFType ref) { this->release(); mRef = ref; return *this; } CFType yield() { CFType r = mRef; mRef = NULL; return r; } CFRef &operator = (CFType ref) { if (ref) CFRetain(ref); return take(ref); } CFRef &operator = (const CFRef &ref) { if (ref) CFRetain(ref); return take(ref); } // take variant for when newly created CFType is returned // via a ptr-to-CFType argument. CFType *take() { if (mRef) CFRelease(mRef); mRef = NULL; return &mRef; } operator CFType () const { return mRef; } operator bool () const { return mRef != NULL; } bool operator ! () const { return mRef == NULL; } CFType get() const { return mRef; } CFType &aref() { take(NULL); return mRef; } CFType retain() const { if (mRef) CFRetain(mRef); return mRef; } void release() const { if (mRef) CFRelease(mRef); } template bool is() const { return CFTraits::check(mRef); } template static CFType check(OldType cf, OSStatus err) { if (cf && !CFTraits::check(cf)) MacOSError::throwMe(err); return CFType(cf); } template NewType as() const { return NewType(mRef); } template NewType as(OSStatus err) const { return CFRef::check(mRef, err); } private: CFType mRef; }; template class CFCopyRef : public CFRef { typedef CFRef _Base; public: CFCopyRef() { } CFCopyRef(CFType ref) : _Base(ref) { this->retain(); } CFCopyRef(const CFCopyRef &ref) : _Base(ref) { this->retain(); } template CFCopyRef(const CFRef<_T> &ref) : _Base(ref) { this->retain(); } CFCopyRef(CFTypeRef ref, OSStatus err) : _Base(ref, err) { this->retain(); } CFCopyRef &take(CFType ref) { _Base::take(ref); return *this; } CFCopyRef &operator = (CFType ref) { if (ref) CFRetain(ref); return take(ref); } CFCopyRef &operator = (const CFCopyRef &ref) { _Base::operator = (ref); return *this; } template CFCopyRef &operator = (const CFRef<_T> &ref) { _Base::operator = (ref); return *this; } }; // // A simple function that turns a non-array CFTypeRef into // an array of one with that element. This will retain its argument // (directly or indirectly). // inline CFArrayRef cfArrayize(CFTypeRef arrayOrItem) { if (arrayOrItem == NULL) return NULL; // NULL is NULL else if (CFGetTypeID(arrayOrItem) == CFArrayGetTypeID()) { CFRetain(arrayOrItem); return CFArrayRef(arrayOrItem); // already an array } else { return CFArrayCreate(NULL, (const void **)&arrayOrItem, 1, &kCFTypeArrayCallBacks); } } // // An empty CFArray. // Since CFArrays are type-neutral, a single immutable empty array will // serve for all uses. So keep it. // struct CFEmptyArray { operator CFArrayRef () { return mArray; } CFEmptyArray(); private: CFArrayRef mArray; }; extern ModuleNexus cfEmptyArray; // // Translate CFStringRef or CFURLRef to (UTF8-encoded) C++ string. // If release==true, a CFRelease will be performed on the CFWhatever argument // whether the call succeeds or not(!). // string cfString(CFStringRef str); // extract UTF8 string string cfString(CFURLRef url); // path of file: URL (only) string cfString(CFBundleRef bundle); // path to bundle root string cfStringRelease(CFStringRef str CF_CONSUMED); // extract UTF8 string string cfStringRelease(CFURLRef url CF_CONSUMED); // path of file: URL (only) string cfStringRelease(CFBundleRef bundle CF_CONSUMED); // path to bundle root string cfString(CFTypeRef anything, OSStatus err); // dynamic form; throws err on NULL // // Handle CFNumberRefs. // This is nasty because CFNumber does not support unsigned types, and there's really no portably-safe // way of working around this. So the handling of unsigned numbers is "almost correct." // template struct CFNumberTraits; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberCharType; typedef char ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberShortType; typedef short ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberIntType; typedef int ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberLongType; typedef long ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberLongLongType; typedef long long ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberFloatType; typedef float ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberDoubleType; typedef double ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberIntType; typedef int ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberIntType; typedef int ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberLongLongType; typedef long long ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberLongLongType; typedef long long ValueType; }; template <> struct CFNumberTraits { static const CFNumberType cfnType = kCFNumberLongLongType; typedef long long ValueType; }; template Number cfNumber(CFNumberRef number) { typename CFNumberTraits::ValueType value; if (CFNumberGetValue(number, CFNumberTraits::cfnType, &value)) return (Number)value; else CFError::throwMe(); } template Number cfNumber(CFNumberRef number, Number defaultValue) { typename CFNumberTraits::ValueType value; if (CFNumberGetValue(number, CFNumberTraits::cfnType, &value)) return value; else return defaultValue; } template CFNumberRef makeCFNumber(Number value) { typename CFNumberTraits::ValueType cfValue = value; return CFNumberCreate(NULL, CFNumberTraits::cfnType, &cfValue); } // legacy form inline uint32_t cfNumber(CFNumberRef number) { return cfNumber(number); } // // Translate strings into CFStrings // inline CFStringRef makeCFString(const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8) { return s ? CFStringCreateWithCString(NULL, s, encoding) : NULL; } inline CFStringRef makeCFString(const string &s, CFStringEncoding encoding = kCFStringEncodingUTF8) { return CFStringCreateWithCString(NULL, s.c_str(), encoding); } inline CFStringRef makeCFString(CFDataRef data, CFStringEncoding encoding = kCFStringEncodingUTF8) { return CFStringCreateFromExternalRepresentation(NULL, data, encoding); } // // Create CFURL objects from various sources // CFURLRef makeCFURL(const char *s, bool isDirectory = false, CFURLRef base = NULL); CFURLRef makeCFURL(CFStringRef s, bool isDirectory = false, CFURLRef base = NULL); inline CFURLRef makeCFURL(const string &s, bool isDirectory = false, CFURLRef base = NULL) { return makeCFURL(s.c_str(), isDirectory, base); } // // Make temporary CF objects. // class CFTempString : public CFRef { public: template CFTempString(Source s) : CFRef(makeCFString(s)) { } }; class CFTempURL : public CFRef { public: template CFTempURL(Source s, bool isDirectory = false, CFURLRef base = NULL) : CFRef(makeCFURL(s, isDirectory, base)) { } }; // // A temporary CFNumber // class CFTempNumber : public CFRef { public: template CFTempNumber(Value value) : CFRef(makeCFNumber(value)) { } }; // // A temporary CFData. // class CFTempData : public CFRef { public: CFTempData(const void *data, size_t length) : CFRef(CFDataCreate(NULL, (const UInt8 *)data, length)) { } template CFTempData(const Dataoid &dataoid) : CFRef(CFDataCreate(NULL, (const UInt8 *)dataoid.data(), dataoid.length())) { } }; class CFTempDataWrap : public CFRef { public: CFTempDataWrap(const void *data, size_t length) : CFRef(CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)data, length, kCFAllocatorNull)) { } template CFTempDataWrap(const Dataoid &dataoid) : CFRef(CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)dataoid.data(), dataoid.length(), kCFAllocatorNull)) { } }; // // Create CFData objects from various sources. // inline CFDataRef makeCFData(const void *data, size_t size) { return CFDataCreate(NULL, (const UInt8 *)data, size); } inline CFDataRef makeCFData(CFDictionaryRef dictionary) { return CFPropertyListCreateXMLData(NULL, dictionary); } template inline CFDataRef makeCFData(const Data &source) { return CFDataCreate(NULL, reinterpret_cast(source.data()), source.length()); } inline CFDataRef makeCFDataMalloc(const void *data, size_t size) { return CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)data, size, kCFAllocatorMalloc); } template inline CFDataRef makeCFDataMalloc(const Data &source) { return CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)source.data(), source.length(), kCFAllocatorMalloc); } // // Create a CFDataRef from malloc'ed data, exception-safely // class CFMallocData { public: CFMallocData(size_t size) : mData(::malloc(size)), mSize(size) { if (!mData) UnixError::throwMe(); } ~CFMallocData() { if (mData) ::free(mData); } template operator T * () { return static_cast(mData); } operator CFDataRef (); void *data() { return mData; } const void *data() const { return mData; } size_t length() const { return mSize; } private: void *mData; size_t mSize; }; // // Make CFDictionaries from stuff // CFDictionaryRef makeCFDictionary(unsigned count, ...); // key/value pairs CFMutableDictionaryRef makeCFMutableDictionary(); // empty CFMutableDictionaryRef makeCFMutableDictionary(unsigned count, ...); // (count) key/value pairs CFMutableDictionaryRef makeCFMutableDictionary(CFDictionaryRef dict); // copy of dictionary CFDictionaryRef makeCFDictionaryFrom(CFDataRef data) CF_RETURNS_RETAINED;// interpret plist form CFDictionaryRef makeCFDictionaryFrom(const void *data, size_t length) CF_RETURNS_RETAINED; // ditto // // Parsing out a CFDictionary without losing your lunch // class CFDictionary : public CFCopyRef { typedef CFCopyRef _Base; public: CFDictionary(CFDictionaryRef ref, OSStatus error) : _Base(ref), mDefaultError(error) { if (!ref) MacOSError::throwMe(error); } CFDictionary(CFTypeRef ref, OSStatus error) : _Base(ref, error), mDefaultError(error) { if (!ref) MacOSError::throwMe(error); } CFDictionary(OSStatus error) : _Base(NULL), mDefaultError(error) { } using CFCopyRef::get; CFTypeRef get(CFStringRef key) { return CFDictionaryGetValue(*this, key); } CFTypeRef get(const char *key) { return CFDictionaryGetValue(*this, CFTempString(key)); } template CFType get(CFStringRef key, OSStatus err = errSecSuccess) const { CFTypeRef elem = CFDictionaryGetValue(*this, key); return CFRef::check(elem, err ? err : mDefaultError); } template CFType get(const char *key, OSStatus err = errSecSuccess) const { return get(CFTempString(key), err); } void apply(CFDictionaryApplierFunction func, void *context) { return CFDictionaryApplyFunction(*this, func, context); } private: template struct Applier { T *object; void (T::*func)(CFTypeRef key, CFTypeRef value); static void apply(CFTypeRef key, CFTypeRef value, void *context) { Applier *me = (Applier *)context; return ((me->object)->*(me->func))(key, value); } }; template struct BlockApplier { void (^action)(Key key, Value value); static void apply(CFTypeRef key, CFTypeRef value, void* context) { BlockApplier *me = (BlockApplier *)context; return me->action(Key(key), Value(value)); } }; public: template void apply(T *object, void (T::*func)(CFTypeRef key, CFTypeRef value)) { Applier app; app.object = object; app.func = func; return apply(app.apply, &app); } template void apply(void (^action)(Key key, Value value)) { BlockApplier app; app.action = action; return apply(app.apply, &app); } private: OSStatus mDefaultError; }; // // CFURLAccess wrappers for specific purposes // CFDataRef cfLoadFile(CFURLRef url); CFDataRef cfLoadFile(int fd, size_t bytes); inline CFDataRef cfLoadFile(CFStringRef path) { return cfLoadFile(CFTempURL(path)); } inline CFDataRef cfLoadFile(const std::string &path) { return cfLoadFile(CFTempURL(path)); } inline CFDataRef cfLoadFile(const char *path) { return cfLoadFile(CFTempURL(path)); } // // Internally used STL adapters. Should probably be in utilities.h. // template Self projectPair(const Self &me) { return me; } template Second projectPair(const pair &me) { return me.second; } // // A CFToVector turns a CFArrayRef of items into a flat // C vector of some type, using a conversion function // (from CFTypeRef) specified. As a special bonus, if // you provide a CFTypeRef (other than CFArrayRef), it // will be transparently handled as an array-of-one. // The array will be automatically released on destruction // of the CFToVector object. Any internal structure shared // with the CFTypeRef inputs will be left alone. // template class CFToVector { public: CFToVector(CFArrayRef arrayRef); ~CFToVector() { delete[] mVector; } operator UInt32 () const { return mCount; } operator VectorBase *() const { return mVector; } bool empty() const { return mCount == 0; } VectorBase *begin() const { return mVector; } VectorBase *end() const { return mVector + mCount; } VectorBase &operator [] (UInt32 ix) const { assert(ix < mCount); return mVector[ix]; } private: VectorBase *mVector; UInt32 mCount; }; template CFToVector::CFToVector(CFArrayRef arrayRef) { if (arrayRef == NULL) { mCount = 0; mVector = NULL; } else { mCount = (UInt32)CFArrayGetCount(arrayRef); mVector = new VectorBase[mCount]; for (UInt32 n = 0; n < mCount; n++) mVector[n] = convert(CFRefType(CFArrayGetValueAtIndex(arrayRef, n))); } } // // Make CFArrays from stuff. // template inline CFArrayRef makeCFArray(Generator &generate, Iterator first, Iterator last) { // how many elements? size_t size = distance(first, last); // do the CFArrayCreate tango auto_array vec(size); for (UInt32 n = 0; n < size; n++) vec[n] = generate(projectPair(*first++)); assert(first == last); return CFArrayCreate(NULL, (const void **)vec.get(), size, &kCFTypeArrayCallBacks); } template inline CFArrayRef makeCFArray(Generator &generate, const Container &container) { return makeCFArray(generate, container.begin(), container.end()); } CFArrayRef makeCFArray(CFIndex count, ...) CF_RETURNS_RETAINED; CFMutableArrayRef makeCFMutableArray(CFIndex count, ...) CF_RETURNS_RETAINED; } // end namespace Security #endif //_H_CFUTILITIES