cfutilities.h   [plain text]


/*
 * Copyright (c) 2000-2004 Apple Computer, 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 <security_utilities/utilities.h>
#include <security_utilities/globalizer.h>
#include <CoreFoundation/CoreFoundation.h>
#include <algorithm>

#undef check


namespace Security {


//
// Traits of popular CF types
//
template <class CFType> struct CFTraits { };

template <> struct CFTraits<CFTypeRef> {
	static bool check(CFTypeRef ref) { return true; }
};

#define __SEC_CFTYPE(name) \
	template <> struct CFTraits<name##Ref> { \
		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(CFURL)
__SEC_CFTYPE(CFBundle)
__SEC_CFTYPE(CFArray)
__SEC_CFTYPE(CFDictionary)
__SEC_CFTYPE(CFSet)



//
// Initialize-only self-releasing CF object handler (lightweight).
//
template <class CFType> class CFRef {
public:
    CFRef() : mRef(NULL) { }
    CFRef(CFType ref) : mRef(ref) { }
    ~CFRef() { this->release(); }
	CFRef(const CFRef &ref) : mRef(ref) {}
	template <class _T> CFRef(const CFRef<_T> &ref) : mRef(ref) {}
	
	CFRef(CFTypeRef ref, OSStatus err)
		: mRef(CFType(ref))
	{
		if (ref && !CFTraits<CFType>::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 <class NewType>
	bool is() const { return CFTraits<NewType>::check(mRef); }
	
	template <class OldType>
	static CFType check(OldType cf, OSStatus err)
	{
		if (cf && !CFTraits<CFType>::check(cf))
			MacOSError::throwMe(err);
		return CFType(cf);
	}
	
	template <class NewType>
	NewType as() const { return NewType(mRef); }
	
	template <class NewType>
	NewType as(OSStatus err) const { return CFRef<NewType>::check(mRef, err); }

private:
    CFType mRef;
};


template <class CFType> class CFCopyRef : public CFRef<CFType> {
	typedef CFRef<CFType> _Base;
public:
    CFCopyRef() { }
    CFCopyRef(CFType ref) : _Base(ref) { this->retain(); }
    CFCopyRef(const CFCopyRef &ref) : _Base(ref) { this->retain(); }
	template <class _T> 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; }
};


//
// 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> 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, bool release = false);	// extract UTF8 string
string cfString(CFURLRef url, bool release = false);	// path of file: URL (only)
string cfString(CFBundleRef url, bool release = false);	// path to bundle root

string cfString(CFTypeRef url, OSStatus err);			// dynamic form; throws on bad type or 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 <class Number>
class CFNumberTraits;

template <> struct CFNumberTraits<char> {
	static const CFNumberType cfnType = kCFNumberCharType;
	typedef char ValueType;
};
template <> struct CFNumberTraits<short> {
	static const CFNumberType cfnType = kCFNumberShortType;
	typedef short ValueType;
};
template <> struct CFNumberTraits<int> {
	static const CFNumberType cfnType = kCFNumberIntType;
	typedef int ValueType;
};
template <> struct CFNumberTraits<long> {
	static const CFNumberType cfnType = kCFNumberLongType;
	typedef long ValueType;
};
template <> struct CFNumberTraits<long long> {
	static const CFNumberType cfnType = kCFNumberLongLongType;
	typedef long long ValueType;
};
template <> struct CFNumberTraits<float> {
	static const CFNumberType cfnType = kCFNumberFloatType;
	typedef float ValueType;
};
template <> struct CFNumberTraits<double> {
	static const CFNumberType cfnType = kCFNumberDoubleType;
	typedef double ValueType;
};

template <> struct CFNumberTraits<unsigned char> {
	static const CFNumberType cfnType = kCFNumberIntType;
	typedef int ValueType;
};
template <> struct CFNumberTraits<unsigned short> {
	static const CFNumberType cfnType = kCFNumberIntType;
	typedef int ValueType;
};
template <> struct CFNumberTraits<unsigned int> {
	static const CFNumberType cfnType = kCFNumberLongLongType;
	typedef long long ValueType;
};
template <> struct CFNumberTraits<unsigned long> {
	static const CFNumberType cfnType = kCFNumberLongLongType;
	typedef long long ValueType;
};
template <> struct CFNumberTraits<unsigned long long> {
	static const CFNumberType cfnType = kCFNumberLongLongType;
	typedef long long ValueType;
};

template <class Number>
Number cfNumber(CFNumberRef number)
{
	typename CFNumberTraits<Number>::ValueType value;
	if (CFNumberGetValue(number, CFNumberTraits<Number>::cfnType, &value))
		return value;
	else
		CFError::throwMe();
}

template <class Number>
Number cfNumber(CFNumberRef number, Number defaultValue)
{
	typename CFNumberTraits<Number>::ValueType value;
	if (CFNumberGetValue(number, CFNumberTraits<Number>::cfnType, &value))
		return value;
	else
		return defaultValue;
}

template <class Number>
CFNumberRef makeCFNumber(Number value)
{
	typename CFNumberTraits<Number>::ValueType cfValue = value;
	return CFNumberCreate(NULL, CFNumberTraits<Number>::cfnType, &cfValue);
}

// legacy form
inline uint32_t cfNumber(CFNumberRef number) { return cfNumber<uint32_t>(number); }


//
// Make temporary CF objects.
//
class CFTempString : public CFRef<CFStringRef> {
public:
	template <class Source>
	CFTempString(Source s) : CFRef<CFStringRef>(makeCFString(s)) { }
};

class CFTempURL : public CFRef<CFURLRef> {
public:
	template <class Source>
	CFTempURL(Source s, bool isDirectory = false, CFURLRef base = NULL)
		: CFRef<CFURLRef>(makeCFURL(s, isDirectory, base)) { }
};


//
// A temporary CFNumber
//
class CFTempNumber : public CFRef<CFNumberRef> {
public:
	template <class Value>
	CFTempNumber(Value value) : CFRef<CFNumberRef>(makeCFNumber(value)) { }
};


//
// A temporary CFData.
//
class CFTempData : public CFRef<CFDataRef> {
public:
	CFTempData(const void *data, size_t length)
		: CFRef<CFDataRef>(CFDataCreate(NULL, (const UInt8 *)data, length)) { }
	
	template <class Dataoid>
	CFTempData(const Dataoid &dataoid)
		: CFRef<CFDataRef>(CFDataCreate(NULL, (const UInt8 *)dataoid.data(), dataoid.length())) { }
};


//
// 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 <class Data>
inline CFDataRef makeCFData(const Data &source)
{
	return CFDataCreate(NULL, reinterpret_cast<const UInt8 *>(source.data()), source.length());
}

inline CFDataRef makeCFDataMalloc(const void *data, size_t size)
{
	return CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)data, size, kCFAllocatorMalloc);
}

template <class Data>
inline CFDataRef makeCFDataMalloc(const Data &source)
{
	return CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)source.data(), source.length(), kCFAllocatorMalloc);
}


//
// 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);
}


//
// 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 <class T>
	operator T * ()
	{ return static_cast<T *>(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);	// interpret plist form
CFDictionaryRef makeCFDictionaryFrom(const void *data, size_t length); // ditto


//
// Parsing out a CFDictionary without losing your lunch
//
class CFDictionary : public CFCopyRef<CFDictionaryRef> {
	typedef CFCopyRef<CFDictionaryRef> _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) { }
	
	CFTypeRef get(CFStringRef key)		{ return CFDictionaryGetValue(*this, key); }
	CFTypeRef get(const char *key)		{ return CFDictionaryGetValue(*this, CFTempString(key)); }
	
	template <class CFType>
	CFType get(CFStringRef key, OSStatus err = noErr) const
	{
		CFTypeRef elem = CFDictionaryGetValue(*this, key);
		return CFRef<CFType>::check(elem, err ? err : mDefaultError);
	}
	
	template <class CFType>
	CFType get(const char *key, OSStatus err = noErr) const
	{ return get<CFType>(CFTempString(key), err); }
	
	void apply(CFDictionaryApplierFunction func, void *context)
	{ return CFDictionaryApplyFunction(*this, func, context); }
	
private:
	template <class T>
	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); }
	};		

public:	
	template <class T>
	void apply(T *object, void (T::*func)(CFTypeRef key, CFTypeRef value))
	{ Applier<T> app; app.object = object; app.func = func; return apply(app.apply, &app); }

private:
	OSStatus mDefaultError;
};


//
// CFURLAccess wrappers for specific purposes
//
CFDataRef cfLoadFile(CFURLRef url);
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 <class Self>
Self projectPair(const Self &me)
{ return me; }

template <class First, class Second>
Second projectPair(const pair<First, Second> &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 VectorBase, class CFRefType, VectorBase convert(CFRefType)>
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 <class VectorBase, class CFRefType, VectorBase convert(CFRefType)>
CFToVector<VectorBase, CFRefType, convert>::CFToVector(CFArrayRef arrayRef)
{
    if (arrayRef == NULL) {
        mCount = 0;
        mVector = NULL;
    } else {
        mCount = 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 <class Iterator, class Generator>
inline CFArrayRef makeCFArray(Generator &generate, Iterator first, Iterator last)
{
	// how many elements?
	size_t size = distance(first, last);
	
	// do the CFArrayCreate tango
    auto_array<CFTypeRef> 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 <class Container, class Generator>
inline CFArrayRef makeCFArray(Generator &generate, const Container &container)
{
	return makeCFArray(generate, container.begin(), container.end());
}

CFArrayRef makeCFArray(CFIndex count, ...);
CFMutableArrayRef makeCFMutableArray(CFIndex count, ...);


} // end namespace Security

#endif //_H_CFUTILITIES