#include "simpleprefs.h"
#include "errors.h"
#include <sys/param.h>
#include <stdlib.h>
#include <assert.h>
#include <stdexcept>
#include <security_utilities/debugging.h>
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFURLAccess.h>
#include <CoreFoundation/CFPropertyList.h>
#include <sys/stat.h>
#define prefsDebug(args...) secinfo("simpleprefs", ## args)
#define kSecUserPrefsDir "Library/Preferences"
#define kSecSystemPrefsDir "/Library/Preferences"
#pragma mark ----- (immutable) Dictionary -----
static void pathForDomain(const char *domain, Dictionary::UserOrSystem userSys, std::string &path)
{
path.clear();
if(userSys == Dictionary::US_User) {
const char *home = getenv("HOME");
if(home == NULL) {
home = "";
}
path = std::string(home) + "/" + kSecUserPrefsDir + "/" + domain + ".plist";
}
else {
path = std::string(kSecSystemPrefsDir) + "/" + domain + ".plist";
}
}
static bool FileExists(const char* s)
{
struct stat st;
int result = stat(s, &st);
return result == 0;
}
Dictionary* Dictionary::CreateDictionary(const char* path)
{
if (!FileExists(path))
{
return NULL;
}
else
{
return new Dictionary(path);
}
}
Dictionary* Dictionary::CreateDictionary(const char* domain, UserOrSystem userSys, bool loose)
{
std::string path;
pathForDomain(domain, userSys, path);
bool exists = FileExists(path.c_str());
if (!loose && !exists)
{
return NULL;
}
if (!exists)
{
return new Dictionary();
}
return new Dictionary(path.c_str());
}
Dictionary::Dictionary() : mDict(NULL)
{
}
Dictionary::Dictionary(
const char *path)
: mDict(NULL)
{
initFromFile(path);
}
Dictionary::Dictionary(
CFDictionaryRef dict)
: mDict(dict)
{
if (mDict)
CFRetain(mDict);
}
Dictionary::~Dictionary()
{
if(mDict) {
CFRelease(mDict);
}
}
const void *Dictionary::getValue(
CFStringRef key)
{
return CFDictionaryGetValue(dict(), key);
}
CFStringRef Dictionary::getStringValue(
CFStringRef key)
{
CFStringRef val = (CFStringRef)CFDictionaryGetValue(dict(), key);
if(val == NULL) {
return NULL;
}
if(CFGetTypeID(val) != CFStringGetTypeID()) {
return NULL;
}
return val;
}
CFDataRef Dictionary::getDataValue(
CFStringRef key)
{
CFDataRef val = (CFDataRef)CFDictionaryGetValue(dict(), key);
if(val == NULL) {
return NULL;
}
if(CFGetTypeID(val) != CFDataGetTypeID()) {
return NULL;
}
return val;
}
CFDictionaryRef Dictionary::getDictValue(
CFStringRef key)
{
CFDictionaryRef val = (CFDictionaryRef)CFDictionaryGetValue(dict(), key);
if(val == NULL) {
return NULL;
}
if(CFGetTypeID(val) != CFDictionaryGetTypeID()) {
return NULL;
}
return val;
}
Dictionary *Dictionary::copyDictValue(
CFStringRef key)
{
CFDictionaryRef cfDict = getDictValue(key);
if(cfDict == NULL) {
return NULL;
}
Dictionary *rtnDict = new Dictionary(cfDict);
return rtnDict;
}
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)
{
if(mDict != NULL)
CFRelease(mDict);
mDict = newDict;
CFRetain(mDict);
}
void Dictionary::initFromFile(
const char *path,
bool loose )
{
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, NULL, &errorCode);
CFRelease(url);
if(success) {
propList = CFPropertyListCreateFromXMLData(
NULL,
fileData,
kCFPropertyListImmutable,
&errorString);
if(propList != NULL) {
mDict = (CFDictionaryRef)propList;
}
else {
success = false;
}
}
if(fileData != NULL) {
CFRelease(fileData);
}
if(errorString != NULL) {
CFRelease(errorString);
}
if(!success) {
if (loose)
return;
else
UnixError::throwMe(EIO);
}
}
#pragma mark ----- Mutable Dictionary -----
MutableDictionary* MutableDictionary::CreateMutableDictionary(const char* fileName)
{
std::string path;
if (!FileExists(path.c_str()))
{
return NULL;
}
else
{
return new MutableDictionary(path.c_str());
}
}
MutableDictionary* MutableDictionary::CreateMutableDictionary(const char *domain, UserOrSystem userSys)
{
std::string path;
pathForDomain(domain, userSys, path);
if (!FileExists(path.c_str()))
{
return NULL;
}
return new MutableDictionary(path.c_str());
}
MutableDictionary::MutableDictionary()
: Dictionary((CFDictionaryRef)CFDictionaryCreateMutable(NULL, 0,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks))
{
CFRelease(mDict);
}
MutableDictionary::MutableDictionary(
const char *filename)
: Dictionary(filename)
{
makeMutable();
}
MutableDictionary::MutableDictionary(
CFDictionaryRef dict,
bool isMutable)
: Dictionary(dict)
{
if(!isMutable) {
makeMutable();
}
}
MutableDictionary::~MutableDictionary()
{
}
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);
}
}
MutableDictionary *MutableDictionary::copyMutableDictValue(
CFStringRef key)
{
CFMutableDictionaryRef cfDict = getMutableDictValue(key);
assert(CFGetRetainCount(cfDict) == 1);
MutableDictionary *rtnDict = new MutableDictionary(cfDict, true);
CFRelease(cfDict);
return rtnDict;
}
void MutableDictionary::setValue(
CFStringRef key,
CFTypeRef val)
{
CFDictionarySetValue(mutableDict(), key, val);
}
void MutableDictionary::setDataValue(
CFStringRef key,
const void *valData, CFIndex valLength)
{
CFDataRef cfVal = CFDataCreate(NULL, reinterpret_cast<const UInt8 *>(valData), valLength);
setValue(key, cfVal);
CFRelease(cfVal);
}
void MutableDictionary::removeValue(
CFStringRef key)
{
CFDictionaryRemoveValue(mutableDict(), key);
}
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;
}
bool MutableDictionary::writePlistToPrefs(
const char *domain, UserOrSystem userSys) {
std::string path;
pathForDomain(domain, userSys, path);
return writePlistToFile(path.c_str());
}
void MutableDictionary::makeMutable()
{
CFMutableDictionaryRef mutDict = CFDictionaryCreateMutableCopy(NULL, 0, dict());
if(mutDict == NULL) {
throw std::bad_alloc();
}
setDict(mutDict);
CFRelease(mutDict);
}