/* * Copyright (c) 2002-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@ */ /* * simpleprefs.cpp - plist support for a bare bones Preferences implementation, * using only Darwin-avaialble CoreFoundation classes. */ #include "simpleprefs.h" #include "errors.h" #include #include #include #include #include #include #include #include #include #define prefsDebug(args...) secdebug("simpleprefs", ## args) #define kSecUserPrefsDir "Library/Preferences" /* relative to $HOME */ #define kSecSystemPrefsDir "/Library/Preferences" #pragma mark ----- (immutable) Dictionary ----- Dictionary::Dictionary( const char *path) : mDict(NULL) { initFromFile(path); } Dictionary::Dictionary( const char *domain, // e.g., com.apple.security UserOrSystem userSys) // US_User : ~/Library/Preferences/domain.plist // US_System: /Library/Preferences/domain.plist : mDict(NULL) { char path[MAXPATHLEN]; pathForDomain(domain, userSys, path); initFromFile(path); } Dictionary::Dictionary( CFDictionaryRef dict) : mDict(dict) { CFRetain(mDict); prefsDebug("Dictionary(%p) this %p cnt %d", dict, this, (int)CFGetRetainCount(mDict)); } Dictionary::~Dictionary() { prefsDebug("~Dictionary this %p cnt %d", this, (int)CFGetRetainCount(mDict)); if(mDict) { CFRelease(mDict); } } /* basic lookup */ const void *Dictionary::getValue( CFStringRef key) { return CFDictionaryGetValue(dict(), key); } /* lookup, value must be CFString (we check) */ CFStringRef Dictionary::getStringValue( CFStringRef key) { CFStringRef val = (CFStringRef)CFDictionaryGetValue(dict(), key); if(val == NULL) { return NULL; } if(CFGetTypeID(val) != CFStringGetTypeID()) { return NULL; } return val; } /* lookup, value must be CFData (we check) */ CFDataRef Dictionary::getDataValue( CFStringRef key) { CFDataRef val = (CFDataRef)CFDictionaryGetValue(dict(), key); if(val == NULL) { return NULL; } if(CFGetTypeID(val) != CFDataGetTypeID()) { return NULL; } return val; } /* lookup, value must be CFDictionary (we check) */ CFDictionaryRef Dictionary::getDictValue( CFStringRef key) { CFDictionaryRef val = (CFDictionaryRef)CFDictionaryGetValue(dict(), key); if(val == NULL) { return NULL; } if(CFGetTypeID(val) != CFDictionaryGetTypeID()) { return NULL; } return val; } /* * Lookup, value is a dictionary, we return value as Dictionary * if found, else return NULL. */ Dictionary *Dictionary::copyDictValue( CFStringRef key) { CFDictionaryRef cfDict = getDictValue(key); if(cfDict == NULL) { return NULL; } Dictionary *rtnDict = new Dictionary(cfDict); /* * mDict has one ref count * cfDict has one ref count */ prefsDebug("copyDictValue this %p cnt NOW %d", this, (int)CFGetRetainCount(mDict)); return rtnDict; } /* * boolean lookup, tolerate many different forms of value. * Default if value not present is false. */ bool Dictionary::getBoolValue( CFStringRef key) { CFTypeRef val = CFDictionaryGetValue(dict(), key); if(val == NULL) { return false; } CFComparisonResult res; if(CFGetTypeID(val) == CFStringGetTypeID()) { res = CFStringCompare((CFStringRef)val, CFSTR("YES"), kCFCompareCaseInsensitive); if(res == kCFCompareEqualTo) { return true; } else { return false; } } if(CFGetTypeID(val) == CFBooleanGetTypeID()) { return CFBooleanGetValue((CFBooleanRef)val) ? true : false; } if(CFGetTypeID(val) == CFNumberGetTypeID()) { char cval = 0; CFNumberGetValue((CFNumberRef)val, kCFNumberCharType, &cval); return (cval == 0) ? false : true; } return false; } CFIndex Dictionary::count() { return CFDictionaryGetCount(dict()); } void Dictionary::setDict( CFDictionaryRef newDict) { prefsDebug("Dictionary::setDict() old %p oldcnt %d new %p newcnt %d", mDict, (int)CFGetRetainCount(mDict), newDict, (int)CFGetRetainCount(newDict)); if(mDict != NULL) { CFRelease(mDict); } mDict = newDict; CFRetain(mDict); } /* fundamental routine to init from a plist file; throws a UnixError on error */ void Dictionary::initFromFile( const char *path) { if(mDict != NULL) { CFRelease(mDict); mDict = NULL; } CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, strlen(path), false); if(url == NULL) { UnixError::throwMe(EIO); } CFDataRef fileData = NULL; CFPropertyListRef propList = NULL; CFStringRef errorString = NULL; SInt32 errorCode; Boolean success = CFURLCreateDataAndPropertiesFromResource( NULL, url, &fileData, NULL, // properties NULL, // desiredProperties &errorCode); CFRelease(url); if(success) { propList = CFPropertyListCreateFromXMLData( NULL, fileData, kCFPropertyListImmutable, &errorString); if(propList != NULL) { /* * Note don't use setDict() here to avoid the extra * refcount that would entail. We own the dictionary now. */ mDict = (CFDictionaryRef)propList; } else { success = false; } } if(fileData != NULL) { CFRelease(fileData); } if(errorString != NULL) { CFRelease(errorString); } if(!success) { UnixError::throwMe(EIO); } prefsDebug("Dictionary::initFromFile(%s) dict %p this %p", path, mDict, this); } void Dictionary::pathForDomain( const char *domain, // e.g., com.apple.security UserOrSystem userSys, // US_User : ~/Library/Preferences/domain.plist // US_System: /Library/Preferences/domain.plist char *path) // mallocd by caller, >= MAXPATHLEN bytes { if(userSys == US_User) { char *home = getenv("HOME"); sprintf(path, "%s/%s/%s.plist", home, kSecUserPrefsDir, domain); } else { sprintf(path, "%s/%s.plist", kSecSystemPrefsDir, domain); } } #pragma mark ----- Mutable Dictionary ----- /* Create an empty mutable dictionary */ MutableDictionary::MutableDictionary() : Dictionary((CFDictionaryRef)CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) { /* lose one of those two retain counts.... */ CFRelease(mDict); prefsDebug("MutableDictionary() dict %p cnt %d this %p", mDict, (int)CFGetRetainCount(mDict), this); } MutableDictionary::MutableDictionary( const char *filename) : Dictionary(filename) { prefsDebug("MutableDictionary(fn %s) dict %p this %p", filename, mDict, this); /* * Dictionary's contructor read the plist from disk. Now * replace that dictionary with a mutable copy. */ makeMutable(); prefsDebug("MutableDictionary(fn %s) new dict %p cnt %d this %p", filename, mDict, (int)CFGetRetainCount(mDict), this); } /* create from preferences file */ MutableDictionary::MutableDictionary( const char *domain, // e.g., com.apple.security UserOrSystem userSys) // US_User : ~/Library/Preferences/domain.plist // US_System: /Library/Preferences/domain.plist : Dictionary(domain, userSys) { prefsDebug("MutableDictionary(dom %s) dict %p this %p", domain, mDict, this); /* ditto the comments in preceeding constructor */ makeMutable(); prefsDebug("MutableDictionary(dom %s) dict %p cnt %d this %p", domain, mDict, (int)CFGetRetainCount(mDict), this); } /* * Create from existing CFDictionary (OR CFMutableDictionary). * I don't see any way the CF runtime will let us differentiate an * immutable from a mutable dictionary here, so caller has to tell us. */ MutableDictionary::MutableDictionary( CFDictionaryRef dict, bool isMutable) : Dictionary(dict) { if(!isMutable) { makeMutable(); } prefsDebug("MutableDictionary(dict %p) new dict %p cnt %d this %p", dict, mDict, (int)CFGetRetainCount(mDict), this); } MutableDictionary::~MutableDictionary() { /* nothing for now */ } /* * Lookup, value must be CFDictionary (we check). We return a * mutable copy, or if key not found, we return a new mutable dictionary * with a ref count of one. * If you want a NULL return if it's not there, use getDictValue(). */ CFMutableDictionaryRef MutableDictionary::getMutableDictValue( CFStringRef key) { CFDictionaryRef dict = getDictValue(key); if(dict == NULL) { prefsDebug("getMutableDictValue returning new empty dict; this %p", this); return CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } else { prefsDebug("getMutableDictValue returning copy; this %p", this); return CFDictionaryCreateMutableCopy(NULL, 0, dict); } } /* * Lookup, value is a dictionary, we return a MutableDictionary, even if * no value found. */ MutableDictionary *MutableDictionary::copyMutableDictValue( CFStringRef key) { CFMutableDictionaryRef cfDict = getMutableDictValue(key); assert(CFGetRetainCount(cfDict) == 1); MutableDictionary *rtnDict = new MutableDictionary(cfDict, true); CFRelease(cfDict); /* rtnDict->mDict now holds the only ref count */ return rtnDict; } /* * Basic setter. Does a replace if present, add if not present op. */ void MutableDictionary::setValue( CFStringRef key, CFTypeRef val) { CFDictionarySetValue(mutableDict(), key, val); } /* * Set key/value pair, data as CFData in the dictionary but passed to us as CSSM_DATA. */ void MutableDictionary::setDataValue( CFStringRef key, const void *valData, CFIndex valLength) { CFDataRef cfVal = CFDataCreate(NULL, reinterpret_cast(valData), valLength); setValue(key, cfVal); CFRelease(cfVal); } /* remove key/value, if present; not an error if it's not */ void MutableDictionary::removeValue( CFStringRef key) { CFDictionaryRemoveValue(mutableDict(), key); } /* write as XML property list, both return true on success */ bool MutableDictionary::writePlistToFile( const char *path) { assert(mDict != NULL); CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, strlen(path), false); if(url == NULL) { UnixError::throwMe(EIO); } CFDataRef xmlData = CFPropertyListCreateXMLData(NULL, dict()); bool ourRtn = false; SInt32 errorCode; if(xmlData == NULL) { goto errOut; } if(CFURLWriteDataAndPropertiesToResource(url, xmlData, NULL, &errorCode)) { ourRtn = true; } errOut: if(url) { CFRelease(url); } if(xmlData) { CFRelease(xmlData); } return ourRtn; } /* write XML property list to preferences file */ bool MutableDictionary::writePlistToPrefs( const char *domain, // e.g., com.apple.security UserOrSystem userSys) // US_User : ~/Library/Preferences/domain.plist // US_System: /Library/Preferences/domain.plist { char path[MAXPATHLEN]; pathForDomain(domain, userSys, path); return writePlistToFile(path); } /* * Called after Dictionary reads plist from file, resulting in an immutable * mDict. We replace that with a mutable copy. */ void MutableDictionary::makeMutable() { CFMutableDictionaryRef mutDict = CFDictionaryCreateMutableCopy(NULL, 0, dict()); if(mutDict == NULL) { throw std::bad_alloc(); } setDict(mutDict); }