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