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