1 /*
2      File: CASettingsStorage.cpp
3  Abstract: CASettingsStorage.h
4   Version: 1.1
5 
6  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
7  Inc. ("Apple") in consideration of your agreement to the following
8  terms, and your use, installation, modification or redistribution of
9  this Apple software constitutes acceptance of these terms.  If you do
10  not agree with these terms, please do not use, install, modify or
11  redistribute this Apple software.
12 
13  In consideration of your agreement to abide by the following terms, and
14  subject to these terms, Apple grants you a personal, non-exclusive
15  license, under Apple's copyrights in this original Apple software (the
16  "Apple Software"), to use, reproduce, modify and redistribute the Apple
17  Software, with or without modifications, in source and/or binary forms;
18  provided that if you redistribute the Apple Software in its entirety and
19  without modifications, you must retain this notice and the following
20  text and disclaimers in all such redistributions of the Apple Software.
21  Neither the name, trademarks, service marks or logos of Apple Inc. may
22  be used to endorse or promote products derived from the Apple Software
23  without specific prior written permission from Apple.  Except as
24  expressly stated in this notice, no other rights or licenses, express or
25  implied, are granted by Apple herein, including but not limited to any
26  patent rights that may be infringed by your derivative works or by other
27  works in which the Apple Software may be incorporated.
28 
29  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
30  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 
35  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42  POSSIBILITY OF SUCH DAMAGE.
43 
44  Copyright (C) 2014 Apple Inc. All Rights Reserved.
45 
46 */
47 //==================================================================================================
48 //	Includes
49 //==================================================================================================
50 
51 //	Self Include
52 #include "CASettingsStorage.h"
53 
54 //	PublicUtility Includes
55 #include "CAAutoDisposer.h"
56 #include "CACFArray.h"
57 #include "CACFData.h"
58 #include "CACFDictionary.h"
59 #include "CACFDistributedNotification.h"
60 #include "CACFNumber.h"
61 
62 //	Stamdard Library Includes
63 #include <string.h>
64 #include <sys/fcntl.h>
65 
66 //==================================================================================================
67 //	CASettingsStorage
68 //==================================================================================================
69 
CASettingsStorage(const char * inSettingsFilePath,mode_t inSettingsFileAccessMode,CFPropertyListFormat inSettingsCacheFormat,bool inIsSingleProcessOnly,bool inIsReadOnly)70 CASettingsStorage::CASettingsStorage(const char* inSettingsFilePath, mode_t inSettingsFileAccessMode, CFPropertyListFormat inSettingsCacheFormat, bool inIsSingleProcessOnly, bool inIsReadOnly)
71 :
72 	mSettingsFilePath(NULL),
73 	mSettingsFileAccessMode(inSettingsFileAccessMode),
74 	mSettingsCache(CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)),
75 	mSettingsCacheFormat(inSettingsCacheFormat),
76 	mSettingsCacheTime(),
77 	mSettingsCacheForceRefresh(true),
78 	mIsSingleProcessOnly(inIsSingleProcessOnly),
79 	mIsReadOnly(inIsReadOnly)
80 {
81 	size_t theLength = strlen(inSettingsFilePath);
82 	mSettingsFilePath = new char[theLength + 2];
83 	strlcpy(mSettingsFilePath, inSettingsFilePath, theLength + 2);
84 
85 	mSettingsCacheTime.tv_sec = 0;
86 	mSettingsCacheTime.tv_nsec = 0;
87 
88 	mSettingsCacheForceRefresh = true;
89 }
90 
~CASettingsStorage()91 CASettingsStorage::~CASettingsStorage()
92 {
93 	delete[] mSettingsFilePath;
94 
95 	if(mSettingsCache != NULL)
96 	{
97 		CFRelease(mSettingsCache);
98 	}
99 }
100 
GetNumberKeys() const101 UInt32	CASettingsStorage::GetNumberKeys() const
102 {
103 	//	make sure our cache is up to date
104 	const_cast<CASettingsStorage*>(this)->RefreshSettings();
105 
106 	return ToUInt32(CFDictionaryGetCount(mSettingsCache));
107 }
108 
GetKeys(UInt32 inNumberKeys,UInt32 & outNumberKeys,CFStringRef * outKeys) const109 void	CASettingsStorage::GetKeys(UInt32 inNumberKeys, UInt32& outNumberKeys, CFStringRef* outKeys) const
110 {
111 	//	make sure our cache is up to date
112 	const_cast<CASettingsStorage*>(this)->RefreshSettings();
113 
114 	CFDictionaryGetKeysAndValues(mSettingsCache, reinterpret_cast<const void**>(outKeys), NULL);
115 	outNumberKeys = inNumberKeys;
116 }
117 
CopyBoolValue(CFStringRef inKey,bool & outValue,bool inDefaultValue) const118 void	CASettingsStorage::CopyBoolValue(CFStringRef inKey, bool& outValue, bool inDefaultValue) const
119 {
120 	//	initialize the return value
121 	outValue = inDefaultValue;
122 
123 	//	get the raw value
124 	CFTypeRef theValue = NULL;
125 	CopyCFTypeValue(inKey, theValue, NULL);
126 
127 	//	for this type, NULL is an invalid value
128 	if(theValue != NULL)
129 	{
130 		//	bools can be made from either CFBooleans or CFNumbers
131 		if(CFGetTypeID(theValue) == CFBooleanGetTypeID())
132 		{
133 			//	get the return value from the CF object
134 			outValue = CFBooleanGetValue(static_cast<CFBooleanRef>(theValue));
135 		}
136 		else if(CFGetTypeID(theValue) == CFNumberGetTypeID())
137 		{
138 			//	get the numeric value
139 			SInt32 theNumericValue = 0;
140 			CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &theNumericValue);
141 
142 			//	non-zero indicates true
143 			outValue = theNumericValue != 0;
144 		}
145 
146 		//	release the value since we aren't returning it
147 		CFRelease(theValue);
148 	}
149 }
150 
CopySInt32Value(CFStringRef inKey,SInt32 & outValue,SInt32 inDefaultValue) const151 void	CASettingsStorage::CopySInt32Value(CFStringRef inKey, SInt32& outValue, SInt32 inDefaultValue) const
152 {
153 	//	initialize the return value
154 	outValue = inDefaultValue;
155 
156 	//	get the raw value
157 	CFTypeRef theValue = NULL;
158 	CopyCFTypeValue(inKey, theValue, NULL);
159 
160 	//	for this type, NULL is an invalid value
161 	if(theValue != NULL)
162 	{
163 		//	make sure we are dealing with the right kind of CF object
164 		if(CFGetTypeID(theValue) == CFNumberGetTypeID())
165 		{
166 			//	get the return value from the CF object
167 			CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
168 		}
169 
170 		//	release the value since we aren't returning it
171 		CFRelease(theValue);
172 	}
173 }
174 
CopyUInt32Value(CFStringRef inKey,UInt32 & outValue,UInt32 inDefaultValue) const175 void	CASettingsStorage::CopyUInt32Value(CFStringRef inKey, UInt32& outValue, UInt32 inDefaultValue) const
176 {
177 	//	initialize the return value
178 	outValue = inDefaultValue;
179 
180 	//	get the raw value
181 	CFTypeRef theValue = NULL;
182 	CopyCFTypeValue(inKey, theValue, NULL);
183 
184 	//	for this type, NULL is an invalid value
185 	if(theValue != NULL)
186 	{
187 		//	make sure we are dealing with the right kind of CF object
188 		if(CFGetTypeID(theValue) == CFNumberGetTypeID())
189 		{
190 			//	get the return value from the CF object
191 			CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
192 		}
193 
194 		//	release the value since we aren't returning it
195 		CFRelease(theValue);
196 	}
197 }
198 
CopySInt64Value(CFStringRef inKey,SInt64 & outValue,SInt64 inDefaultValue) const199 void	CASettingsStorage::CopySInt64Value(CFStringRef inKey, SInt64& outValue, SInt64 inDefaultValue) const
200 {
201 	//	initialize the return value
202 	outValue = inDefaultValue;
203 
204 	//	get the raw value
205 	CFTypeRef theValue = NULL;
206 	CopyCFTypeValue(inKey, theValue, NULL);
207 
208 	//	for this type, NULL is an invalid value
209 	if(theValue != NULL)
210 	{
211 		//	make sure we are dealing with the right kind of CF object
212 		if(CFGetTypeID(theValue) == CFNumberGetTypeID())
213 		{
214 			//	get the return value from the CF object
215 			CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &outValue);
216 		}
217 
218 		//	release the value since we aren't returning it
219 		CFRelease(theValue);
220 	}
221 }
222 
CopyUInt64Value(CFStringRef inKey,UInt64 & outValue,UInt64 inDefaultValue) const223 void	CASettingsStorage::CopyUInt64Value(CFStringRef inKey, UInt64& outValue, UInt64 inDefaultValue) const
224 {
225 	//	initialize the return value
226 	outValue = inDefaultValue;
227 
228 	//	get the raw value
229 	CFTypeRef theValue = NULL;
230 	CopyCFTypeValue(inKey, theValue, NULL);
231 
232 	//	for this type, NULL is an invalid value
233 	if(theValue != NULL)
234 	{
235 		//	make sure we are dealing with the right kind of CF object
236 		if(CFGetTypeID(theValue) == CFNumberGetTypeID())
237 		{
238 			//	get the return value from the CF object
239 			CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &outValue);
240 		}
241 
242 		//	release the value since we aren't returning it
243 		CFRelease(theValue);
244 	}
245 }
246 
CopyFloat32Value(CFStringRef inKey,Float32 & outValue,Float32 inDefaultValue) const247 void	CASettingsStorage::CopyFloat32Value(CFStringRef inKey, Float32& outValue, Float32 inDefaultValue) const
248 {
249 	//	initialize the return value
250 	outValue = inDefaultValue;
251 
252 	//	get the raw value
253 	CFTypeRef theValue = NULL;
254 	CopyCFTypeValue(inKey, theValue, NULL);
255 
256 	//	for this type, NULL is an invalid value
257 	if(theValue != NULL)
258 	{
259 		//	make sure we are dealing with the right kind of CF object
260 		if(CFGetTypeID(theValue) == CFNumberGetTypeID())
261 		{
262 			//	get the return value from the CF object
263 			CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberFloat32Type, &outValue);
264 		}
265 
266 		//	release the value since we aren't returning it
267 		CFRelease(theValue);
268 	}
269 }
270 
CopyFloat64Value(CFStringRef inKey,Float64 & outValue,Float64 inDefaultValue) const271 void	CASettingsStorage::CopyFloat64Value(CFStringRef inKey, Float64& outValue, Float64 inDefaultValue) const
272 {
273 	//	initialize the return value
274 	outValue = inDefaultValue;
275 
276 	//	get the raw value
277 	CFTypeRef theValue = NULL;
278 	CopyCFTypeValue(inKey, theValue, NULL);
279 
280 	//	for this type, NULL is an invalid value
281 	if(theValue != NULL)
282 	{
283 		//	make sure we are dealing with the right kind of CF object
284 		if(CFGetTypeID(theValue) == CFNumberGetTypeID())
285 		{
286 			//	get the return value from the CF object
287 			CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberFloat64Type, &outValue);
288 		}
289 
290 		//	release the value since we aren't returning it
291 		CFRelease(theValue);
292 	}
293 }
294 
CopyNumberValue(CFStringRef inKey,CFNumberRef & outValue,CFNumberRef inDefaultValue) const295 void	CASettingsStorage::CopyNumberValue(CFStringRef inKey, CFNumberRef& outValue, CFNumberRef inDefaultValue) const
296 {
297 	//	initialize the return value
298 	outValue = NULL;
299 
300 	//	get the raw value
301 	CFTypeRef theValue = NULL;
302 	CopyCFTypeValue(inKey, theValue, inDefaultValue);
303 
304 	//	for this type, NULL is a valid value, but requires less work
305 	if(theValue != NULL)
306 	{
307 		//	make sure we are dealing with the right kind of CF object
308 		if(CFGetTypeID(theValue) == CFNumberGetTypeID())
309 		{
310 			//	set the return value to the CF object we are returning
311 			outValue = static_cast<CFNumberRef>(theValue);
312 		}
313 		else
314 		{
315 			//	release the value since we aren't returning it
316 			CFRelease(theValue);
317 
318 			//	set the return value to the default value
319 			outValue = inDefaultValue;
320 
321 			//	and retain it
322 			CFRetain(outValue);
323 		}
324 	}
325 }
326 
CopyStringValue(CFStringRef inKey,CFStringRef & outValue,CFStringRef inDefaultValue) const327 void	CASettingsStorage::CopyStringValue(CFStringRef inKey, CFStringRef& outValue, CFStringRef inDefaultValue) const
328 {
329 	//	initialize the return value
330 	outValue = NULL;
331 
332 	//	get the raw value
333 	CFTypeRef theValue = NULL;
334 	CopyCFTypeValue(inKey, theValue, inDefaultValue);
335 
336 	//	for this type, NULL is a valid value, but requires less work
337 	if(theValue != NULL)
338 	{
339 		//	make sure we are dealing with the right kind of CF object
340 		if(CFGetTypeID(theValue) == CFStringGetTypeID())
341 		{
342 			//	set the return value to the CF object we are returning
343 			outValue = static_cast<CFStringRef>(theValue);
344 		}
345 		else
346 		{
347 			//	release the value since we aren't returning it
348 			CFRelease(theValue);
349 
350 			//	set the return value to the default value
351 			outValue = inDefaultValue;
352 
353 			//	and retain it
354 			CFRetain(outValue);
355 		}
356 	}
357 }
358 
CopyArrayValue(CFStringRef inKey,CFArrayRef & outValue,CFArrayRef inDefaultValue) const359 void	CASettingsStorage::CopyArrayValue(CFStringRef inKey, CFArrayRef& outValue, CFArrayRef inDefaultValue) const
360 {
361 	//	initialize the return value
362 	outValue = NULL;
363 
364 	//	get the raw value
365 	CFTypeRef theValue = NULL;
366 	CopyCFTypeValue(inKey, theValue, inDefaultValue);
367 
368 	//	for this type, NULL is a valid value, but requires less work
369 	if(theValue != NULL)
370 	{
371 		//	make sure we are dealing with the right kind of CF object
372 		if(CFGetTypeID(theValue) == CFArrayGetTypeID())
373 		{
374 			//	set the return value to the CF object we are returning
375 			outValue = static_cast<CFArrayRef>(theValue);
376 		}
377 		else
378 		{
379 			//	release the value since we aren't returning it
380 			CFRelease(theValue);
381 
382 			//	set the return value to the default value
383 			outValue = inDefaultValue;
384 
385 			//	and retain it
386 			CFRetain(outValue);
387 		}
388 	}
389 }
390 
CopyDictionaryValue(CFStringRef inKey,CFDictionaryRef & outValue,CFDictionaryRef inDefaultValue) const391 void	CASettingsStorage::CopyDictionaryValue(CFStringRef inKey, CFDictionaryRef& outValue, CFDictionaryRef inDefaultValue) const
392 {
393 	//	initialize the return value
394 	outValue = NULL;
395 
396 	//	get the raw value
397 	CFTypeRef theValue = NULL;
398 	CopyCFTypeValue(inKey, theValue, inDefaultValue);
399 
400 	//	for this type, NULL is a valid value, but requires less work
401 	if(theValue != NULL)
402 	{
403 		//	make sure we are dealing with the right kind of CF object
404 		if(CFGetTypeID(theValue) == CFDictionaryGetTypeID())
405 		{
406 			//	set the return value to the CF object we are returning
407 			outValue = static_cast<CFDictionaryRef>(theValue);
408 		}
409 		else
410 		{
411 			//	release the value since we aren't returning it
412 			CFRelease(theValue);
413 
414 			//	set the return value to the default value
415 			outValue = inDefaultValue;
416 
417 			//	and retain it
418 			CFRetain(outValue);
419 		}
420 	}
421 }
422 
CopyDataValue(CFStringRef inKey,CFDataRef & outValue,CFDataRef inDefaultValue) const423 void	CASettingsStorage::CopyDataValue(CFStringRef inKey, CFDataRef& outValue, CFDataRef inDefaultValue) const
424 {
425 	//	initialize the return value
426 	outValue = NULL;
427 
428 	//	get the raw value
429 	CFTypeRef theValue = NULL;
430 	CopyCFTypeValue(inKey, theValue, inDefaultValue);
431 
432 	//	for this type, NULL is a valid value, but requires less work
433 	if(theValue != NULL)
434 	{
435 		//	make sure we are dealing with the right kind of CF object
436 		if(CFGetTypeID(theValue) == CFDataGetTypeID())
437 		{
438 			//	set the return value to the CF object we are returning
439 			outValue = static_cast<CFDataRef>(theValue);
440 		}
441 		else
442 		{
443 			//	release the value since we aren't returning it
444 			CFRelease(theValue);
445 
446 			//	set the return value to the default value
447 			outValue = inDefaultValue;
448 
449 			//	and retain it
450 			CFRetain(outValue);
451 		}
452 	}
453 }
454 
CopyCFTypeValue(CFStringRef inKey,CFTypeRef & outValue,CFTypeRef inDefaultValue) const455 void	CASettingsStorage::CopyCFTypeValue(CFStringRef inKey, CFTypeRef& outValue, CFTypeRef inDefaultValue) const
456 {
457 	//	make sure our cache is up to date
458 	const_cast<CASettingsStorage*>(this)->RefreshSettings();
459 
460 	//	check to see if we have a value for the given key
461 	if(!CFDictionaryGetValueIfPresent(mSettingsCache, inKey, &outValue))
462 	{
463 		//	the key wasn't in the cache, so return the default value
464 		outValue = inDefaultValue;
465 	}
466 
467 	//	make sure we retain the return value
468 	if(outValue != NULL)
469 	{
470 		CFRetain(outValue);
471 	}
472 }
473 
SetSInt32Value(CFStringRef inKey,SInt32 inValue)474 void	CASettingsStorage::SetSInt32Value(CFStringRef inKey, SInt32 inValue)
475 {
476 	CACFNumber theValue(inValue);
477 	SetCFTypeValue(inKey, theValue.GetCFNumber());
478 }
479 
SetUInt32Value(CFStringRef inKey,UInt32 inValue)480 void	CASettingsStorage::SetUInt32Value(CFStringRef inKey, UInt32 inValue)
481 {
482 	CACFNumber theValue(inValue);
483 	SetCFTypeValue(inKey, theValue.GetCFNumber());
484 }
485 
SetSInt64Value(CFStringRef inKey,SInt64 inValue)486 void	CASettingsStorage::SetSInt64Value(CFStringRef inKey, SInt64 inValue)
487 {
488 	CACFNumber theValue(inValue);
489 	SetCFTypeValue(inKey, theValue.GetCFNumber());
490 }
491 
SetUInt64Value(CFStringRef inKey,UInt64 inValue)492 void	CASettingsStorage::SetUInt64Value(CFStringRef inKey, UInt64 inValue)
493 {
494 	CACFNumber theValue(inValue);
495 	SetCFTypeValue(inKey, theValue.GetCFNumber());
496 }
497 
SetFloat32Value(CFStringRef inKey,Float32 inValue)498 void	CASettingsStorage::SetFloat32Value(CFStringRef inKey, Float32 inValue)
499 {
500 	CACFNumber theValue(inValue);
501 	SetCFTypeValue(inKey, theValue.GetCFNumber());
502 }
503 
SetFloat64Value(CFStringRef inKey,Float64 inValue)504 void	CASettingsStorage::SetFloat64Value(CFStringRef inKey, Float64 inValue)
505 {
506 	CACFNumber theValue(inValue);
507 	SetCFTypeValue(inKey, theValue.GetCFNumber());
508 }
509 
SetNumberValue(CFStringRef inKey,CFNumberRef inValue)510 void	CASettingsStorage::SetNumberValue(CFStringRef inKey, CFNumberRef inValue)
511 {
512 	SetCFTypeValue(inKey, inValue);
513 }
514 
SetStringValue(CFStringRef inKey,CFStringRef inValue)515 void	CASettingsStorage::SetStringValue(CFStringRef inKey, CFStringRef inValue)
516 {
517 	SetCFTypeValue(inKey, inValue);
518 }
519 
SetArrayValue(CFStringRef inKey,CFArrayRef inValue)520 void	CASettingsStorage::SetArrayValue(CFStringRef inKey, CFArrayRef inValue)
521 {
522 	SetCFTypeValue(inKey, inValue);
523 }
524 
SetDictionaryValue(CFStringRef inKey,CFDictionaryRef inValue)525 void	CASettingsStorage::SetDictionaryValue(CFStringRef inKey, CFDictionaryRef inValue)
526 {
527 	SetCFTypeValue(inKey, inValue);
528 }
529 
SetDataValue(CFStringRef inKey,CFDataRef inValue)530 void	CASettingsStorage::SetDataValue(CFStringRef inKey, CFDataRef inValue)
531 {
532 	SetCFTypeValue(inKey, inValue);
533 }
534 
SetCFTypeValue(CFStringRef inKey,CFTypeRef inValue)535 void	CASettingsStorage::SetCFTypeValue(CFStringRef inKey, CFTypeRef inValue)
536 {
537 	//	make sure our cache is up to date
538 	RefreshSettings();
539 
540 	//	add the new key/value to the dictionary
541 	CFDictionarySetValue(mSettingsCache, inKey, inValue);
542 
543 	//	write the settings to the file
544 	SaveSettings();
545 }
546 
RemoveValue(CFStringRef inKey)547 void	CASettingsStorage::RemoveValue(CFStringRef inKey)
548 {
549 	//	make sure our cache is up to date
550 	RefreshSettings();
551 
552 	//	remove the given key
553 	CFDictionaryRemoveValue(mSettingsCache, inKey);
554 
555 	//	write the settings to the file
556 	SaveSettings();
557 }
558 
RemoveAllValues()559 void	CASettingsStorage::RemoveAllValues()
560 {
561 	//	make sure our cache is up to date
562 	RefreshSettings();
563 
564 	//	remove the given key
565 	CFDictionaryRemoveAllValues(mSettingsCache);
566 
567 	//	write the settings to the file
568 	SaveSettings();
569 }
570 
SendNotification(CFStringRef inName,CFDictionaryRef inData,bool inPostToAllSessions) const571 void	CASettingsStorage::SendNotification(CFStringRef inName, CFDictionaryRef inData, bool inPostToAllSessions) const
572 {
573 	CACFDistributedNotification::PostNotification(inName, inData, inPostToAllSessions);
574 }
575 
ForceRefresh()576 void	CASettingsStorage::ForceRefresh()
577 {
578 	mSettingsCacheForceRefresh = true;
579 }
580 
operator <(const struct timespec & inX,const struct timespec & inY)581 inline bool	operator<(const struct timespec& inX, const struct timespec& inY)
582 {
583 	return ((inX.tv_sec < inY.tv_sec) || ((inX.tv_sec == inY.tv_sec) && (inX.tv_nsec < inY.tv_nsec)));
584 }
585 
RefreshSettings()586 void	CASettingsStorage::RefreshSettings()
587 {
588 	//	if this storage is only supporting a single process, there is no need to hit the disk unless
589 	//	required to by it being the first time or if the refresh is specifically forced for some reason
590 	if(!mIsSingleProcessOnly || (mSettingsCache == NULL) || ((mSettingsCacheTime.tv_sec == 0) && (mSettingsCacheTime.tv_nsec == 0)) || mSettingsCacheForceRefresh)
591 	{
592 		//	first, we need to stat the file to check the mod date, this has the side effect of also
593 		//	telling us if the file exisits
594 		struct stat theFileInfo;
595 		int theStatError = stat(mSettingsFilePath, &theFileInfo);
596 
597 		//	we use this boolean to make error recovery easier since we need a case for when there's no file anyway
598 		bool theSettingsWereCached = false;
599 		bool theSettingsNeedSaving = true;
600 
601 		if(theStatError == 0)
602 		{
603 			//	stat says there is something there, only have to do work if we either don't have a cache or the cache is out of date
604 			if((mSettingsCache == NULL) || (mSettingsCacheTime < theFileInfo.st_mtimespec) || mSettingsCacheForceRefresh)
605 			{
606 				//	open the file
607 				FILE* theFile = fopen(mSettingsFilePath, "r");
608 				if(theFile != NULL)
609 				{
610 					//	lock the file (this call blocks until the lock is taken)
611 					int theError = flock(fileno(theFile), LOCK_EX);
612 					if(theError == 0)
613 					{
614 						//	get the length of the file
615 						fseek(theFile, 0, SEEK_END);
616 						size_t theFileLength = static_cast<size_t>(ftell(theFile));
617 						fseek(theFile, 0, SEEK_SET);
618 
619 						if(theFileLength > 0)
620 						{
621 							//	allocate a block of memory to hold the data in the file
622 							CAAutoFree<Byte> theRawFileData(theFileLength);
623 
624 							//	read all the data in
625 							fread(static_cast<Byte*>(theRawFileData), theFileLength, 1, theFile);
626 
627 							//	release the lock
628 							flock(fileno(theFile), LOCK_UN);
629 
630 							//	put it into a CFData object
631 							CACFData theRawFileDataCFData(static_cast<Byte*>(theRawFileData), static_cast<UInt32>(theFileLength));
632 
633 							//	get rid of the existing cache
634 							if(mSettingsCache != NULL)
635 							{
636 								CFRelease(mSettingsCache);
637 								mSettingsCache = NULL;
638 							}
639 
640 							//	parse the data as a property list
641 							mSettingsCache = (CFMutableDictionaryRef)CFPropertyListCreateWithData(NULL, theRawFileDataCFData.GetCFData(), kCFPropertyListMutableContainersAndLeaves, NULL, NULL);
642 
643 							//	check to be sure we parsed a plist out of the file
644 							if(mSettingsCache != NULL)
645 							{
646 								//	save the date of the cache
647 								mSettingsCacheTime = theFileInfo.st_mtimespec;
648 
649 								//	mark that we're done
650 								theSettingsWereCached = true;
651 								theSettingsNeedSaving = false;
652 							}
653 						}
654 					}
655 
656 					//	close the file
657 					fclose(theFile);
658 					mSettingsCacheForceRefresh = false;
659 				}
660 			}
661 			else
662 			{
663 				//	nothing to do since the file was older than the cached data
664 				theSettingsNeedSaving = false;
665 				theSettingsWereCached = true;
666 			}
667 		}
668 
669 		//	if there was a failure, we need to clean up
670 		if((theStatError != 0) || theSettingsNeedSaving || !theSettingsWereCached)
671 		{
672 			//	we get here if either there isn't a file or something wacky happenned while parsing it
673 			//	so, make sure we have a valid cache dictionary
674 			if(mSettingsCache == NULL)
675 			{
676 				mSettingsCache = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
677 			}
678 
679 			mSettingsCacheTime.tv_sec = 0;
680 			mSettingsCacheTime.tv_nsec = 0;
681 
682 			if((theStatError != 0) || theSettingsNeedSaving)
683 			{
684 				SaveSettings();
685 			}
686 		}
687 	}
688 }
689 
SaveSettings()690 void	CASettingsStorage::SaveSettings()
691 {
692 	if(!mIsReadOnly && (mSettingsCache != NULL))
693 	{
694 		//	make a CFData that contains the new settings
695 		CACFData theNewRawPrefsCFData(CFPropertyListCreateData(NULL, mSettingsCache, mSettingsCacheFormat, 0, NULL), true);
696 
697 		//	open the file for writing
698 		FILE* theFile = fopen(mSettingsFilePath, "w+");
699 		if(theFile != NULL)
700 		{
701 			//	lock the file (this call blocks until the lock is taken)
702 			int theError = flock(fileno(theFile), LOCK_EX);
703 			if(theError == 0)
704 			{
705 				//	set the file access mode if necessary
706 				if(mSettingsFileAccessMode != 0)
707 				{
708 					fchmod(fileno(theFile), mSettingsFileAccessMode);
709 				}
710 
711 				//	write the data
712 				fwrite(theNewRawPrefsCFData.GetDataPtr(), theNewRawPrefsCFData.GetSize(), 1, theFile);
713 
714 				//	flush the file to be sure it is all on disk
715 				fflush(theFile);
716 
717 				//	release the lock
718 				flock(fileno(theFile), LOCK_UN);
719 
720 				//	close the file
721 				fclose(theFile);
722 
723 				//	stat the file to get the mod date
724 				struct stat theFileInfo;
725 				stat(mSettingsFilePath, &theFileInfo);
726 
727 				//	save the mod date
728 				mSettingsCacheTime = theFileInfo.st_mtimespec;
729 			}
730 			else
731 			{
732 				//	close the file
733 				fclose(theFile);
734 			}
735 		}
736 	}
737 }
738