2 * Copyright (c) 2002-2004,2011,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * simpleprefs.cpp - plist support for a bare bones Preferences implementation,
26 * using only Darwin-avaialble CoreFoundation classes.
29 #include "simpleprefs.h"
31 #include <sys/param.h>
35 #include <security_utilities/debugging.h>
36 #include <CoreFoundation/CFData.h>
37 #include <CoreFoundation/CFNumber.h>
38 #include <CoreFoundation/CFURLAccess.h>
39 #include <CoreFoundation/CFPropertyList.h>
42 #define prefsDebug(args...) secdebug("simpleprefs", ## args)
44 #define kSecUserPrefsDir "Library/Preferences" /* relative to $HOME */
45 #define kSecSystemPrefsDir "/Library/Preferences"
47 #pragma mark ----- (immutable) Dictionary -----
49 static void pathForDomain(const char *domain
, Dictionary::UserOrSystem userSys
, std::string
&path
)
52 if(userSys
== Dictionary::US_User
) {
53 const char *home
= getenv("HOME");
57 path
= std::string(home
) + "/" + kSecUserPrefsDir
+ "/" + domain
+ ".plist";
60 path
= std::string(kSecSystemPrefsDir
) + "/" + domain
+ ".plist";
64 static bool FileExists(const char* s
)
66 // this isn't very efficient, either, but orders are to get rid of exceptions...
68 int result
= stat(s
, &st
);
72 // use factory functions to create the dictionaries so that we can test for the presence of the dictionaries
74 Dictionary
* Dictionary::CreateDictionary(const char* path
)
76 if (!FileExists(path
))
82 return new Dictionary(path
);
86 Dictionary
* Dictionary::CreateDictionary(const char* domain
, UserOrSystem userSys
, bool loose
)
89 pathForDomain(domain
, userSys
, path
);
90 bool exists
= FileExists(path
.c_str());
91 if (!loose
&& !exists
)
98 return new Dictionary();
101 return new Dictionary(path
.c_str());
104 Dictionary::Dictionary() : mDict(NULL
)
108 Dictionary::Dictionary(
115 Dictionary::Dictionary(
116 CFDictionaryRef dict
)
123 Dictionary::~Dictionary()
131 const void *Dictionary::getValue(
134 return CFDictionaryGetValue(dict(), key
);
137 /* lookup, value must be CFString (we check) */
138 CFStringRef
Dictionary::getStringValue(
141 CFStringRef val
= (CFStringRef
)CFDictionaryGetValue(dict(), key
);
145 if(CFGetTypeID(val
) != CFStringGetTypeID()) {
151 /* lookup, value must be CFData (we check) */
152 CFDataRef
Dictionary::getDataValue(
155 CFDataRef val
= (CFDataRef
)CFDictionaryGetValue(dict(), key
);
159 if(CFGetTypeID(val
) != CFDataGetTypeID()) {
165 /* lookup, value must be CFDictionary (we check) */
166 CFDictionaryRef
Dictionary::getDictValue(
169 CFDictionaryRef val
= (CFDictionaryRef
)CFDictionaryGetValue(dict(), key
);
173 if(CFGetTypeID(val
) != CFDictionaryGetTypeID()) {
180 * Lookup, value is a dictionary, we return value as Dictionary
181 * if found, else return NULL.
183 Dictionary
*Dictionary::copyDictValue(
186 CFDictionaryRef cfDict
= getDictValue(key
);
190 Dictionary
*rtnDict
= new Dictionary(cfDict
);
192 * mDict has one ref count
193 * cfDict has one ref count
199 * boolean lookup, tolerate many different forms of value.
200 * Default if value not present is false.
202 bool Dictionary::getBoolValue(
205 CFTypeRef val
= CFDictionaryGetValue(dict(), key
);
209 CFComparisonResult res
;
210 if(CFGetTypeID(val
) == CFStringGetTypeID()) {
211 res
= CFStringCompare((CFStringRef
)val
, CFSTR("YES"),
212 kCFCompareCaseInsensitive
);
213 if(res
== kCFCompareEqualTo
) {
220 if(CFGetTypeID(val
) == CFBooleanGetTypeID()) {
221 return CFBooleanGetValue((CFBooleanRef
)val
) ? true : false;
223 if(CFGetTypeID(val
) == CFNumberGetTypeID()) {
225 CFNumberGetValue((CFNumberRef
)val
, kCFNumberCharType
, &cval
);
226 return (cval
== 0) ? false : true;
231 CFIndex
Dictionary::count()
233 return CFDictionaryGetCount(dict());
236 void Dictionary::setDict(
237 CFDictionaryRef newDict
)
245 /* fundamental routine to init from a plist file; throws a UnixError on error */
246 void Dictionary::initFromFile(
248 bool loose
/* = false */)
254 CFURLRef url
= CFURLCreateFromFileSystemRepresentation(NULL
, (const UInt8
*)path
,
255 strlen(path
), false);
257 UnixError::throwMe(EIO
);
260 CFDataRef fileData
= NULL
;
261 CFPropertyListRef propList
= NULL
;
262 CFStringRef errorString
= NULL
;
265 Boolean success
= CFURLCreateDataAndPropertiesFromResource(
270 NULL
, // desiredProperties
274 propList
= CFPropertyListCreateFromXMLData(
277 kCFPropertyListImmutable
,
279 if(propList
!= NULL
) {
281 * Note don't use setDict() here to avoid the extra
282 * refcount that would entail. We own the dictionary now.
284 mDict
= (CFDictionaryRef
)propList
;
290 if(fileData
!= NULL
) {
293 if(errorString
!= NULL
) {
294 CFRelease(errorString
);
300 UnixError::throwMe(EIO
);
304 #pragma mark ----- Mutable Dictionary -----
307 MutableDictionary
* MutableDictionary::CreateMutableDictionary(const char* fileName
)
311 if (!FileExists(path
.c_str()))
317 return new MutableDictionary(path
.c_str());
321 MutableDictionary
* MutableDictionary::CreateMutableDictionary(const char *domain
, UserOrSystem userSys
)
324 pathForDomain(domain
, userSys
, path
);
326 if (!FileExists(path
.c_str()))
331 return new MutableDictionary(path
.c_str());
334 /* Create an empty mutable dictionary */
335 MutableDictionary::MutableDictionary()
336 : Dictionary((CFDictionaryRef
)CFDictionaryCreateMutable(NULL
, 0,
337 &kCFCopyStringDictionaryKeyCallBacks
,
338 &kCFTypeDictionaryValueCallBacks
))
340 /* lose one of those two retain counts.... */
344 MutableDictionary::MutableDictionary(
345 const char *filename
)
346 : Dictionary(filename
)
349 * Dictionary's contructor read the plist from disk. Now
350 * replace that dictionary with a mutable copy.
356 * Create from existing CFDictionary (OR CFMutableDictionary).
357 * I don't see any way the CF runtime will let us differentiate an
358 * immutable from a mutable dictionary here, so caller has to tell us.
360 MutableDictionary::MutableDictionary(
361 CFDictionaryRef dict
,
370 MutableDictionary::~MutableDictionary()
372 /* nothing for now */
376 * Lookup, value must be CFDictionary (we check). We return a
377 * mutable copy, or if key not found, we return a new mutable dictionary
378 * with a ref count of one.
379 * If you want a NULL return if it's not there, use getDictValue().
381 CFMutableDictionaryRef
MutableDictionary::getMutableDictValue(
384 CFDictionaryRef dict
= getDictValue(key
);
386 prefsDebug("getMutableDictValue returning new empty dict; this %p", this);
387 return CFDictionaryCreateMutable(NULL
, 0,
388 &kCFCopyStringDictionaryKeyCallBacks
,
389 &kCFTypeDictionaryValueCallBacks
);
392 prefsDebug("getMutableDictValue returning copy; this %p", this);
393 return CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
398 * Lookup, value is a dictionary, we return a MutableDictionary, even if
401 MutableDictionary
*MutableDictionary::copyMutableDictValue(
404 CFMutableDictionaryRef cfDict
= getMutableDictValue(key
);
405 assert(CFGetRetainCount(cfDict
) == 1);
406 MutableDictionary
*rtnDict
= new MutableDictionary(cfDict
, true);
408 /* rtnDict->mDict now holds the only ref count */
413 * Basic setter. Does a replace if present, add if not present op.
415 void MutableDictionary::setValue(
419 CFDictionarySetValue(mutableDict(), key
, val
);
423 * Set key/value pair, data as CFData in the dictionary but passed to us as CSSM_DATA.
425 void MutableDictionary::setDataValue(
427 const void *valData
, CFIndex valLength
)
429 CFDataRef cfVal
= CFDataCreate(NULL
, reinterpret_cast<const UInt8
*>(valData
), valLength
);
430 setValue(key
, cfVal
);
434 /* remove key/value, if present; not an error if it's not */
435 void MutableDictionary::removeValue(
438 CFDictionaryRemoveValue(mutableDict(), key
);
441 /* write as XML property list, both return true on success */
442 bool MutableDictionary::writePlistToFile(
445 assert(mDict
!= NULL
);
446 CFURLRef url
= CFURLCreateFromFileSystemRepresentation(NULL
, (const UInt8
*)path
,
447 strlen(path
), false);
449 UnixError::throwMe(EIO
);
452 CFDataRef xmlData
= CFPropertyListCreateXMLData(NULL
, dict());
455 if(xmlData
== NULL
) {
458 if(CFURLWriteDataAndPropertiesToResource(url
, xmlData
, NULL
, &errorCode
)) {
471 /* write XML property list to preferences file */
472 bool MutableDictionary::writePlistToPrefs(
473 const char *domain
, // e.g., com.apple.security
474 UserOrSystem userSys
) // US_User : ~/Library/Preferences/domain.plist
475 // US_System: /Library/Preferences/domain.plist
478 pathForDomain(domain
, userSys
, path
);
479 return writePlistToFile(path
.c_str());
483 * Called after Dictionary reads plist from file, resulting in an immutable
484 * mDict. We replace that with a mutable copy.
486 void MutableDictionary::makeMutable()
488 CFMutableDictionaryRef mutDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict());
489 if(mutDict
== NULL
) {
490 throw std::bad_alloc();
493 /* we own the dictionary now */