1/* 2 * Copyright (c) 2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* CFPropertyList.c 25 Copyright (c) 1999-2013, Apple Inc. All rights reserved. 26 Responsibility: Tony Parker 27*/ 28 29#include <CoreFoundation/CFPropertyList.h> 30#include <CoreFoundation/CFDate.h> 31#include <CoreFoundation/CFNumber.h> 32#include <CoreFoundation/CFSet.h> 33#include <CoreFoundation/CFError.h> 34#include <CoreFoundation/CFError_Private.h> 35#include <CoreFoundation/CFPriv.h> 36#include <CoreFoundation/CFStringEncodingConverter.h> 37#include "CFInternal.h" 38#include <CoreFoundation/CFBurstTrie.h> 39#include <CoreFoundation/CFString.h> 40#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 41#include <CoreFoundation/CFStream.h> 42#endif 43#include <CoreFoundation/CFCalendar.h> 44#include "CFLocaleInternal.h" 45#include <limits.h> 46#include <float.h> 47#include <string.h> 48#include <stdlib.h> 49#include <math.h> 50#include <ctype.h> 51 52 53CF_EXPORT CFNumberType _CFNumberGetType2(CFNumberRef number); 54 55#define PLIST_IX 0 56#define ARRAY_IX 1 57#define DICT_IX 2 58#define KEY_IX 3 59#define STRING_IX 4 60#define DATA_IX 5 61#define DATE_IX 6 62#define REAL_IX 7 63#define INTEGER_IX 8 64#define TRUE_IX 9 65#define FALSE_IX 10 66#define DOCTYPE_IX 11 67#define CDSECT_IX 12 68 69#define PLIST_TAG_LENGTH 5 70#define ARRAY_TAG_LENGTH 5 71#define DICT_TAG_LENGTH 4 72#define KEY_TAG_LENGTH 3 73#define STRING_TAG_LENGTH 6 74#define DATA_TAG_LENGTH 4 75#define DATE_TAG_LENGTH 4 76#define REAL_TAG_LENGTH 4 77#define INTEGER_TAG_LENGTH 7 78#define TRUE_TAG_LENGTH 4 79#define FALSE_TAG_LENGTH 5 80#define DOCTYPE_TAG_LENGTH 7 81#define CDSECT_TAG_LENGTH 9 82 83#if !defined(new_cftype_array) 84#define new_cftype_array(N, C) \ 85 size_t N ## _count__ = (C); \ 86 if (N ## _count__ > LONG_MAX / sizeof(CFTypeRef)) { \ 87 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \ 88 HALT; \ 89 } \ 90 Boolean N ## _is_stack__ = (N ## _count__ <= 256); \ 91 if (N ## _count__ == 0) N ## _count__ = 1; \ 92 STACK_BUFFER_DECL(CFTypeRef, N ## _buffer__, N ## _is_stack__ ? N ## _count__ : 1); \ 93 if (N ## _is_stack__) memset(N ## _buffer__, 0, N ## _count__ * sizeof(CFTypeRef)); \ 94 CFTypeRef * N = N ## _is_stack__ ? N ## _buffer__ : (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, (N ## _count__) * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory); \ 95 if (! N) { \ 96 CRSetCrashLogMessage("CFPropertyList ran out of memory while attempting to allocate temporary storage."); \ 97 HALT; \ 98 } \ 99 do {} while (0) 100#endif 101 102#if !defined(free_cftype_array) 103#define free_cftype_array(N) \ 104 if (! N ## _is_stack__) { \ 105 CFAllocatorDeallocate(kCFAllocatorSystemDefault, N); \ 106 } \ 107 do {} while (0) 108#endif 109 110// Used to reference an old-style plist parser error inside of a more general XML error 111#define CFPropertyListOldStyleParserErrorKey CFSTR("kCFPropertyListOldStyleParsingError") 112 113static CFTypeID stringtype, datatype, numbertype, datetype; 114static CFTypeID booltype, nulltype, dicttype, arraytype, settype; 115 116static void initStatics() { 117 static dispatch_once_t once; 118 dispatch_once(&once, ^{ 119 stringtype = CFStringGetTypeID(); 120 datatype = CFDataGetTypeID(); 121 numbertype = CFNumberGetTypeID(); 122 booltype = CFBooleanGetTypeID(); 123 datetype = CFDateGetTypeID(); 124 dicttype = CFDictionaryGetTypeID(); 125 arraytype = CFArrayGetTypeID(); 126 settype = CFSetGetTypeID(); 127 nulltype = CFNullGetTypeID(); 128 }); 129} 130 131CF_PRIVATE CFErrorRef __CFPropertyListCreateError(CFIndex code, CFStringRef debugString, ...) { 132 va_list argList; 133 CFErrorRef error = NULL; 134 135 if (debugString != NULL) { 136 CFStringRef debugMessage = NULL; 137 va_start(argList, debugString); 138 debugMessage = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, NULL, debugString, argList); 139 va_end(argList); 140 141 CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 142 CFDictionarySetValue(userInfo, kCFErrorDebugDescriptionKey, debugMessage); 143 144 error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainCocoa, code, userInfo); 145 146 CFRelease(debugMessage); 147 CFRelease(userInfo); 148 } else { 149 error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainCocoa, code, NULL); 150 } 151 152 return error; 153} 154 155static CFStringRef __copyErrorDebugDescription(CFErrorRef error) { 156 CFStringRef result = NULL; 157 if (error) { 158 CFDictionaryRef userInfo = CFErrorCopyUserInfo(error); 159 if (userInfo != NULL) { 160 CFStringRef desc = (CFStringRef) CFDictionaryGetValue(userInfo, kCFErrorDebugDescriptionKey); 161 if (desc != NULL) { 162 result = CFStringCreateCopy(kCFAllocatorSystemDefault, desc); 163 } 164 CFRelease(userInfo); 165 } 166 } 167 return result; 168} 169 170#pragma mark - 171#pragma mark Property List Validation 172 173// don't allow _CFKeyedArchiverUID here 174#define __CFAssertIsPList(cf) CFAssert2(CFGetTypeID(cf) == CFStringGetTypeID() || CFGetTypeID(cf) == CFArrayGetTypeID() || CFGetTypeID(cf) == CFBooleanGetTypeID() || CFGetTypeID(cf) == CFNumberGetTypeID() || CFGetTypeID(cf) == CFDictionaryGetTypeID() || CFGetTypeID(cf) == CFDateGetTypeID() || CFGetTypeID(cf) == CFDataGetTypeID(), __kCFLogAssertion, "%s(): %p not of a property list type", __PRETTY_FUNCTION__, cf); 175 176struct context { 177 bool answer; 178 CFMutableSetRef set; 179 CFPropertyListFormat format; 180 CFStringRef *error; 181}; 182 183static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format, CFStringRef *error); 184 185static void __CFPropertyListIsArrayPlistAux(const void *value, void *context) { 186 struct context *ctx = (struct context *)context; 187 if (!ctx->answer) return; 188 if (!value && !*(ctx->error)) { 189 *(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list arrays cannot contain NULL")); 190 } 191 ctx->answer = value && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format, ctx->error); 192} 193 194static void __CFPropertyListIsDictPlistAux(const void *key, const void *value, void *context) { 195 struct context *ctx = (struct context *)context; 196 if (!ctx->answer) return; 197 if (!key && !*(ctx->error)) *(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list dictionaries cannot contain NULL keys")); 198 if (!value && !*(ctx->error)) *(ctx->error) = (CFStringRef)CFRetain(CFSTR("property list dictionaries cannot contain NULL values")); 199 if (stringtype != CFGetTypeID(key) && !*(ctx->error)) { 200 CFStringRef desc = CFCopyTypeIDDescription(CFGetTypeID(key)); 201 *(ctx->error) = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("property list dictionaries may only have keys which are CFStrings, not '%@'"), desc); 202 CFRelease(desc); 203 } 204 ctx->answer = key && value && (stringtype == CFGetTypeID(key)) && __CFPropertyListIsValidAux(value, true, ctx->set, ctx->format, ctx->error); 205} 206 207static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format, CFStringRef *error) { 208 CFTypeID type; 209 if (!plist) { 210 *error = (CFStringRef)CFRetain(CFSTR("property lists cannot contain NULL")); 211 return false; 212 } 213 type = CFGetTypeID(plist); 214 if (stringtype == type) return true; 215 if (datatype == type) return true; 216 if (kCFPropertyListOpenStepFormat != format) { 217 if (booltype == type) return true; 218 if (numbertype == type) return true; 219 if (datetype == type) return true; 220 if (_CFKeyedArchiverUIDGetTypeID() == type) return true; 221 } 222 if (!recursive && arraytype == type) return true; 223 if (!recursive && dicttype == type) return true; 224 // at any one invocation of this function, set should contain the objects in the "path" down to this object 225 if (CFSetContainsValue(set, plist)) { 226 *error = (CFStringRef)CFRetain(CFSTR("property lists cannot contain recursive container references")); 227 return false; 228 } 229 if (arraytype == type) { 230 struct context ctx = {true, set, format, error}; 231 CFSetAddValue(set, plist); 232 CFArrayApplyFunction((CFArrayRef)plist, CFRangeMake(0, CFArrayGetCount((CFArrayRef)plist)), __CFPropertyListIsArrayPlistAux, &ctx); 233 CFSetRemoveValue(set, plist); 234 return ctx.answer; 235 } 236 if (dicttype == type) { 237 struct context ctx = {true, set, format, error}; 238 CFSetAddValue(set, plist); 239 CFDictionaryApplyFunction((CFDictionaryRef)plist, __CFPropertyListIsDictPlistAux, &ctx); 240 CFSetRemoveValue(set, plist); 241 return ctx.answer; 242 } 243 244 CFStringRef desc = CFCopyTypeIDDescription(type); 245 *error = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("property lists cannot contain objects of type '%@'"), desc); 246 CFRelease(desc); 247 248 return false; 249} 250 251static Boolean _CFPropertyListIsValidWithErrorString(CFPropertyListRef plist, CFPropertyListFormat format, CFStringRef *error) { 252 initStatics(); 253 CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__); 254 CFMutableSetRef set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); 255 bool result = __CFPropertyListIsValidAux(plist, true, set, format, error); 256 CFRelease(set); 257 return result; 258} 259 260#pragma mark - 261#pragma mark Writing Property Lists 262 263static const char CFXMLPlistTags[13][10]= { 264{'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'}, 265{'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'}, 266{'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'}, 267{'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}, 268{'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'}, 269{'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'}, 270{'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'}, 271{'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'}, 272{'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'}, 273{'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'}, 274{'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'}, 275{'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'}, 276{'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'} 277}; 278 279static const UniChar CFXMLPlistTagsUnicode[13][10]= { 280 {'p', 'l', 'i', 's', 't', '\0', '\0', '\0', '\0', '\0'}, 281 {'a', 'r', 'r', 'a', 'y', '\0', '\0', '\0', '\0', '\0'}, 282 {'d', 'i', 'c', 't', '\0', '\0', '\0', '\0', '\0', '\0'}, 283 {'k', 'e', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0'}, 284 {'s', 't', 'r', 'i', 'n', 'g', '\0', '\0', '\0', '\0'}, 285 {'d', 'a', 't', 'a', '\0', '\0', '\0', '\0', '\0', '\0'}, 286 {'d', 'a', 't', 'e', '\0', '\0', '\0', '\0', '\0', '\0'}, 287 {'r', 'e', 'a', 'l', '\0', '\0', '\0', '\0', '\0', '\0'}, 288 {'i', 'n', 't', 'e', 'g', 'e', 'r', '\0', '\0', '\0'}, 289 {'t', 'r', 'u', 'e', '\0', '\0', '\0', '\0', '\0', '\0'}, 290 {'f', 'a', 'l', 's', 'e', '\0', '\0', '\0', '\0', '\0'}, 291 {'D', 'O', 'C', 'T', 'Y', 'P', 'E', '\0', '\0', '\0'}, 292 {'<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[', '\0'} 293}; 294 295typedef struct { 296 const char *begin; // first character of the XML to be parsed 297 const char *curr; // current parse location 298 const char *end; // the first character _after_ the end of the XML 299 CFErrorRef error; 300 CFAllocatorRef allocator; 301 UInt32 mutabilityOption; 302 CFBurstTrieRef stringTrie; // map of cached strings 303 CFMutableArrayRef stringCache; // retaining array of strings 304 Boolean allowNewTypes; // Whether to allow the new types supported by XML property lists, but not by the old, OPENSTEP ASCII property lists (CFNumber, CFBoolean, CFDate) 305 CFSetRef keyPaths; // if NULL, no filtering 306 Boolean skip; // if true, do not create any objects. 307} _CFXMLPlistParseInfo; 308 309CF_PRIVATE CFTypeRef __CFCreateOldStylePropertyListOrStringsFile(CFAllocatorRef allocator, CFDataRef xmlData, CFStringRef originalString, CFStringEncoding guessedEncoding, CFOptionFlags option, CFErrorRef *outError,CFPropertyListFormat *format); 310 311CF_INLINE void __CFPListRelease(CFTypeRef cf, CFAllocatorRef allocator) { 312 if (cf && !(0)) CFRelease(cf); 313} 314 315 316// The following set of _plist... functions append various things to a mutable data which is in UTF8 encoding. These are pretty general. Assumption is call characters and CFStrings can be converted to UTF8 and appeneded. 317 318// Null-terminated, ASCII or UTF8 string 319// 320static void _plistAppendUTF8CString(CFMutableDataRef mData, const char *cString) { 321 CFDataAppendBytes (mData, (const UInt8 *)cString, strlen(cString)); 322} 323 324// UniChars 325// 326static void _plistAppendCharacters(CFMutableDataRef mData, const UniChar *chars, CFIndex length) { 327 CFIndex curLoc = 0; 328 329 do { // Flush out ASCII chars, BUFLEN at a time 330 #define BUFLEN 400 331 UInt8 buf[BUFLEN], *bufPtr = buf; 332 CFIndex cnt = 0; 333 while (cnt < length && (cnt - curLoc < BUFLEN) && (chars[cnt] < 128)) *bufPtr++ = (UInt8)(chars[cnt++]); 334 if (cnt > curLoc) { // Flush any ASCII bytes 335 CFDataAppendBytes(mData, buf, cnt - curLoc); 336 curLoc = cnt; 337 } 338 } while (curLoc < length && (chars[curLoc] < 128)); // We will exit out of here when we run out of chars or hit a non-ASCII char 339 340 if (curLoc < length) { // Now deal with non-ASCII chars 341 CFDataRef data = NULL; 342 CFStringRef str = NULL; 343 if ((str = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, chars + curLoc, length - curLoc, kCFAllocatorNull))) { 344 if ((data = CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8, 0))) { 345 CFDataAppendBytes (mData, CFDataGetBytePtr(data), CFDataGetLength(data)); 346 CFRelease(data); 347 } 348 CFRelease(str); 349 } 350 CFAssert1(str && data, __kCFLogAssertion, "%s(): Error writing plist", __PRETTY_FUNCTION__); 351 } 352} 353 354// Append CFString 355// 356static void _plistAppendString(CFMutableDataRef mData, CFStringRef str) { 357 const UniChar *chars; 358 const char *cStr; 359 CFDataRef data; 360 if ((chars = CFStringGetCharactersPtr(str))) { 361 _plistAppendCharacters(mData, chars, CFStringGetLength(str)); 362 } else if ((cStr = CFStringGetCStringPtr(str, kCFStringEncodingASCII)) || (cStr = CFStringGetCStringPtr(str, kCFStringEncodingUTF8))) { 363 _plistAppendUTF8CString(mData, cStr); 364 } else if ((data = CFStringCreateExternalRepresentation(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8, 0))) { 365 CFDataAppendBytes (mData, CFDataGetBytePtr(data), CFDataGetLength(data)); 366 CFRelease(data); 367 } else { 368 CFAssert1(TRUE, __kCFLogAssertion, "%s(): Error in plist writing", __PRETTY_FUNCTION__); 369 } 370} 371 372 373// Append CFString-style format + arguments 374// 375static void _plistAppendFormat(CFMutableDataRef mData, CFStringRef format, ...) { 376 CFStringRef fStr; 377 va_list argList; 378 379 va_start(argList, format); 380 fStr = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, NULL, format, argList); 381 va_end(argList); 382 383 CFAssert1(fStr, __kCFLogAssertion, "%s(): Error writing plist", __PRETTY_FUNCTION__); 384 _plistAppendString(mData, fStr); 385 CFRelease(fStr); 386} 387 388 389 390static void _appendIndents(CFIndex numIndents, CFMutableDataRef str) { 391#define NUMTABS 4 392 static const UniChar tabs[NUMTABS] = {'\t','\t','\t','\t'}; 393 for (; numIndents > 0; numIndents -= NUMTABS) _plistAppendCharacters(str, tabs, (numIndents >= NUMTABS) ? NUMTABS : numIndents); 394} 395 396/* Append the escaped version of origStr to mStr. 397*/ 398static void _appendEscapedString(CFStringRef origStr, CFMutableDataRef mStr) { 399#define BUFSIZE 64 400 CFIndex i, length = CFStringGetLength(origStr); 401 CFIndex bufCnt = 0; 402 UniChar buf[BUFSIZE]; 403 CFStringInlineBuffer inlineBuffer; 404 405 CFStringInitInlineBuffer(origStr, &inlineBuffer, CFRangeMake(0, length)); 406 407 for (i = 0; i < length; i ++) { 408 UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&inlineBuffer, i); 409 if (CFStringIsSurrogateHighCharacter(ch) && (bufCnt + 2 >= BUFSIZE)) { 410 // flush the buffer first so we have room for a low/high pair and do not split them 411 _plistAppendCharacters(mStr, buf, bufCnt); 412 bufCnt = 0; 413 } 414 415 switch(ch) { 416 case '<': 417 if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); 418 bufCnt = 0; 419 _plistAppendUTF8CString(mStr, "<"); 420 break; 421 case '>': 422 if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); 423 bufCnt = 0; 424 _plistAppendUTF8CString(mStr, ">"); 425 break; 426 case '&': 427 if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); 428 bufCnt = 0; 429 _plistAppendUTF8CString(mStr, "&"); 430 break; 431 default: 432 buf[bufCnt++] = ch; 433 if (bufCnt == BUFSIZE) { 434 _plistAppendCharacters(mStr, buf, bufCnt); 435 bufCnt = 0; 436 } 437 break; 438 } 439 } 440 if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt); 441} 442 443 444 445/* Base-64 encoding/decoding */ 446 447/* The base-64 encoding packs three 8-bit bytes into four 7-bit ASCII 448 * characters. If the number of bytes in the original data isn't divisable 449 * by three, "=" characters are used to pad the encoded data. The complete 450 * set of characters used in base-64 are: 451 * 452 * 'A'..'Z' => 00..25 453 * 'a'..'z' => 26..51 454 * '0'..'9' => 52..61 455 * '+' => 62 456 * '/' => 63 457 * '=' => pad 458 */ 459 460// Write the inputData to the mData using Base 64 encoding 461 462static void _XMLPlistAppendDataUsingBase64(CFMutableDataRef mData, CFDataRef inputData, CFIndex indent) { 463 static const char __CFPLDataEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 464 #define MAXLINELEN 76 465 char buf[MAXLINELEN + 4 + 2]; // For the slop and carriage return and terminating NULL 466 467 const uint8_t *bytes = CFDataGetBytePtr(inputData); 468 CFIndex length = CFDataGetLength(inputData); 469 CFIndex i, pos; 470 const uint8_t *p; 471 472 if (indent > 8) indent = 8; // refuse to indent more than 64 characters 473 474 pos = 0; // position within buf 475 476 for (i = 0, p = bytes; i < length; i++, p++) { 477 /* 3 bytes are encoded as 4 */ 478 switch (i % 3) { 479 case 0: 480 buf[pos++] = __CFPLDataEncodeTable [ ((p[0] >> 2) & 0x3f)]; 481 break; 482 case 1: 483 buf[pos++] = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 4) & 0x3f)]; 484 break; 485 case 2: 486 buf[pos++] = __CFPLDataEncodeTable [ ((((p[-1] << 8) | p[0]) >> 6) & 0x3f)]; 487 buf[pos++] = __CFPLDataEncodeTable [ (p[0] & 0x3f)]; 488 break; 489 } 490 /* Flush the line out every 76 (or fewer) chars --- indents count against the line length*/ 491 if (pos >= MAXLINELEN - 8 * indent) { 492 buf[pos++] = '\n'; 493 buf[pos++] = 0; 494 _appendIndents(indent, mData); 495 _plistAppendUTF8CString(mData, buf); 496 pos = 0; 497 } 498 } 499 500 switch (i % 3) { 501 case 0: 502 break; 503 case 1: 504 buf[pos++] = __CFPLDataEncodeTable [ ((p[-1] << 4) & 0x30)]; 505 buf[pos++] = '='; 506 buf[pos++] = '='; 507 break; 508 case 2: 509 buf[pos++] = __CFPLDataEncodeTable [ ((p[-1] << 2) & 0x3c)]; 510 buf[pos++] = '='; 511 break; 512 } 513 514 if (pos > 0) { 515 buf[pos++] = '\n'; 516 buf[pos++] = 0; 517 _appendIndents(indent, mData); 518 _plistAppendUTF8CString(mData, buf); 519 } 520} 521 522extern CFStringRef __CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf); 523 524static void _CFAppendXML0(CFTypeRef object, UInt32 indentation, CFMutableDataRef xmlString) { 525 UInt32 typeID = CFGetTypeID(object); 526 _appendIndents(indentation, xmlString); 527 if (typeID == stringtype) { 528 _plistAppendUTF8CString(xmlString, "<"); 529 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[STRING_IX], STRING_TAG_LENGTH); 530 _plistAppendUTF8CString(xmlString, ">"); 531 _appendEscapedString((CFStringRef)object, xmlString); 532 _plistAppendUTF8CString(xmlString, "</"); 533 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[STRING_IX], STRING_TAG_LENGTH); 534 _plistAppendUTF8CString(xmlString, ">\n"); 535 } else if (typeID == arraytype) { 536 UInt32 i, count = CFArrayGetCount((CFArrayRef)object); 537 if (count == 0) { 538 _plistAppendUTF8CString(xmlString, "<"); 539 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[ARRAY_IX], ARRAY_TAG_LENGTH); 540 _plistAppendUTF8CString(xmlString, "/>\n"); 541 return; 542 } 543 _plistAppendUTF8CString(xmlString, "<"); 544 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[ARRAY_IX], ARRAY_TAG_LENGTH); 545 _plistAppendUTF8CString(xmlString, ">\n"); 546 for (i = 0; i < count; i ++) { 547 _CFAppendXML0(CFArrayGetValueAtIndex((CFArrayRef)object, i), indentation+1, xmlString); 548 } 549 _appendIndents(indentation, xmlString); 550 _plistAppendUTF8CString(xmlString, "</"); 551 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[ARRAY_IX], ARRAY_TAG_LENGTH); 552 _plistAppendUTF8CString(xmlString, ">\n"); 553 } else if (typeID == dicttype) { 554 UInt32 i, count = CFDictionaryGetCount((CFDictionaryRef)object); 555 CFMutableArrayRef keyArray; 556 if (count == 0) { 557 _plistAppendUTF8CString(xmlString, "<"); 558 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DICT_IX], DICT_TAG_LENGTH); 559 _plistAppendUTF8CString(xmlString, "/>\n"); 560 return; 561 } 562 _plistAppendUTF8CString(xmlString, "<"); 563 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DICT_IX], DICT_TAG_LENGTH); 564 _plistAppendUTF8CString(xmlString, ">\n"); 565 new_cftype_array(keys, count); 566 CFDictionaryGetKeysAndValues((CFDictionaryRef)object, keys, NULL); 567 keyArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, count, &kCFTypeArrayCallBacks); 568 CFArrayReplaceValues(keyArray, CFRangeMake(0, 0), keys, count); 569 CFArraySortValues(keyArray, CFRangeMake(0, count), (CFComparatorFunction)CFStringCompare, NULL); 570 CFArrayGetValues(keyArray, CFRangeMake(0, count), keys); 571 CFRelease(keyArray); 572 for (i = 0; i < count; i ++) { 573 CFTypeRef key = keys[i]; 574 _appendIndents(indentation+1, xmlString); 575 _plistAppendUTF8CString(xmlString, "<"); 576 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[KEY_IX], KEY_TAG_LENGTH); 577 _plistAppendUTF8CString(xmlString, ">"); 578 _appendEscapedString((CFStringRef)key, xmlString); 579 _plistAppendUTF8CString(xmlString, "</"); 580 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[KEY_IX], KEY_TAG_LENGTH); 581 _plistAppendUTF8CString(xmlString, ">\n"); 582 _CFAppendXML0(CFDictionaryGetValue((CFDictionaryRef)object, key), indentation+1, xmlString); 583 } 584 free_cftype_array(keys); 585 _appendIndents(indentation, xmlString); 586 _plistAppendUTF8CString(xmlString, "</"); 587 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DICT_IX], DICT_TAG_LENGTH); 588 _plistAppendUTF8CString(xmlString, ">\n"); 589 } else if (typeID == datatype) { 590 _plistAppendUTF8CString(xmlString, "<"); 591 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DATA_IX], DATA_TAG_LENGTH); 592 _plistAppendUTF8CString(xmlString, ">\n"); 593 _XMLPlistAppendDataUsingBase64(xmlString, (CFDataRef)object, indentation); 594 _appendIndents(indentation, xmlString); 595 _plistAppendUTF8CString(xmlString, "</"); 596 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DATA_IX], DATA_TAG_LENGTH); 597 _plistAppendUTF8CString(xmlString, ">\n"); 598 } else if (typeID == datetype) { 599 // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z' 600 int32_t y = 0, M = 0, d = 0, H = 0, m = 0, s = 0; 601#if 1 602 CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime((CFDateRef)object), NULL); 603 y = date.year; 604 M = date.month; 605 d = date.day; 606 H = date.hour; 607 m = date.minute; 608 s = (int32_t)date.second; 609#else 610 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFCalendarIdentifierGregorian); 611 CFTimeZoneRef tz = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, CFSTR("GMT"), true); 612 CFCalendarSetTimeZone(calendar, tz); 613 CFCalendarDecomposeAbsoluteTime(calendar, CFDateGetAbsoluteTime((CFDateRef)object), (const uint8_t *)"yMdHms", &y, &M, &d, &H, &m, &s); 614 CFRelease(calendar); 615 CFRelease(tz); 616#endif 617 _plistAppendUTF8CString(xmlString, "<"); 618 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DATE_IX], DATE_TAG_LENGTH); 619 _plistAppendUTF8CString(xmlString, ">"); 620 _plistAppendFormat(xmlString, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), y, M, d, H, m, s); 621 _plistAppendUTF8CString(xmlString, "</"); 622 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[DATE_IX], DATE_TAG_LENGTH); 623 _plistAppendUTF8CString(xmlString, ">\n"); 624 } else if (typeID == numbertype) { 625 if (CFNumberIsFloatType((CFNumberRef)object)) { 626 _plistAppendUTF8CString(xmlString, "<"); 627 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[REAL_IX], REAL_TAG_LENGTH); 628 _plistAppendUTF8CString(xmlString, ">"); 629 CFStringRef s = __CFNumberCopyFormattingDescriptionAsFloat64(object); 630 _plistAppendString(xmlString, s); 631 CFRelease(s); 632 _plistAppendUTF8CString(xmlString, "</"); 633 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[REAL_IX], REAL_TAG_LENGTH); 634 _plistAppendUTF8CString(xmlString, ">\n"); 635 } else { 636 _plistAppendUTF8CString(xmlString, "<"); 637 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[INTEGER_IX], INTEGER_TAG_LENGTH); 638 _plistAppendUTF8CString(xmlString, ">"); 639 640 _plistAppendFormat(xmlString, CFSTR("%@"), object); 641 642 _plistAppendUTF8CString(xmlString, "</"); 643 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[INTEGER_IX], INTEGER_TAG_LENGTH); 644 _plistAppendUTF8CString(xmlString, ">\n"); 645 } 646 } else if (typeID == booltype) { 647 if (CFBooleanGetValue((CFBooleanRef)object)) { 648 _plistAppendUTF8CString(xmlString, "<"); 649 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[TRUE_IX], TRUE_TAG_LENGTH); 650 _plistAppendUTF8CString(xmlString, "/>\n"); 651 } else { 652 _plistAppendUTF8CString(xmlString, "<"); 653 _plistAppendCharacters(xmlString, CFXMLPlistTagsUnicode[FALSE_IX], FALSE_TAG_LENGTH); 654 _plistAppendUTF8CString(xmlString, "/>\n"); 655 } 656 } 657} 658 659static void _CFGenerateXMLPropertyListToData(CFMutableDataRef xml, CFTypeRef propertyList) { 660 _plistAppendUTF8CString(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE "); 661 _plistAppendCharacters(xml, CFXMLPlistTagsUnicode[PLIST_IX], PLIST_TAG_LENGTH); 662 _plistAppendUTF8CString(xml, " PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<"); 663 _plistAppendCharacters(xml, CFXMLPlistTagsUnicode[PLIST_IX], PLIST_TAG_LENGTH); 664 _plistAppendUTF8CString(xml, " version=\"1.0\">\n"); 665 666 _CFAppendXML0(propertyList, 0, xml); 667 668 _plistAppendUTF8CString(xml, "</"); 669 _plistAppendCharacters(xml, CFXMLPlistTagsUnicode[PLIST_IX], PLIST_TAG_LENGTH); 670 _plistAppendUTF8CString(xml, ">\n"); 671} 672 673// ======================================================================== 674#pragma mark - 675#pragma mark Exported Creation Functions 676 677CFDataRef _CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList, Boolean checkValidPlist) { 678 initStatics(); 679 CFMutableDataRef xml; 680 CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); 681 if (checkValidPlist && !CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0)) { 682 __CFAssertIsPList(propertyList); 683 return NULL; 684 } 685 xml = CFDataCreateMutable(allocator, 0); 686 _CFGenerateXMLPropertyListToData(xml, propertyList); 687 return xml; 688} 689 690CFDataRef CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList) { 691 return _CFPropertyListCreateXMLData(allocator, propertyList, true); 692} 693 694CF_EXPORT CFDataRef _CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator, CFPropertyListRef propertyList) { 695 return _CFPropertyListCreateXMLData(allocator, propertyList, false); 696} 697 698Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat format) { 699 initStatics(); 700 CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__); 701 CFMutableSetRef set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); 702 CFStringRef error = NULL; 703 bool result = __CFPropertyListIsValidAux(plist, true, set, format, &error); 704 if (error) { 705#if defined(DEBUG) 706 CFLog(kCFLogLevelWarning, CFSTR("CFPropertyListIsValid(): %@"), error); 707#endif 708 CFRelease(error); 709 } 710 CFRelease(set); 711 return result; 712} 713 714// ======================================================================== 715#pragma mark - 716#pragma mark Reading Plists 717 718// 719// ------------------------- Reading plists ------------------ 720// 721 722static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo); 723static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out); 724 725// warning: doesn't have a good idea of Unicode line separators 726static UInt32 lineNumber(_CFXMLPlistParseInfo *pInfo) { 727 const char *p = pInfo->begin; 728 UInt32 count = 1; 729 while (p < pInfo->curr) { 730 if (*p == '\r') { 731 count ++; 732 if (*(p + 1) == '\n') 733 p ++; 734 } else if (*p == '\n') { 735 count ++; 736 } 737 p ++; 738 } 739 return count; 740} 741 742// warning: doesn't have a good idea of Unicode white space 743CF_INLINE void skipWhitespace(_CFXMLPlistParseInfo *pInfo) { 744 while (pInfo->curr < pInfo->end) { 745 switch (*(pInfo->curr)) { 746 case ' ': 747 case '\t': 748 case '\n': 749 case '\r': 750 pInfo->curr ++; 751 continue; 752 default: 753 return; 754 } 755 } 756} 757 758/* All of these advance to the end of the given construct and return a pointer to the first character beyond the construct. If the construct doesn't parse properly, NULL is returned. */ 759 760// pInfo should be just past "<!--" 761static void skipXMLComment(_CFXMLPlistParseInfo *pInfo) { 762 const char *p = pInfo->curr; 763 const char *end = pInfo->end - 3; // Need at least 3 characters to compare against 764 while (p < end) { 765 if (*p == '-' && *(p+1) == '-' && *(p+2) == '>') { 766 pInfo->curr = p+3; 767 return; 768 } 769 p ++; 770 } 771 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unterminated comment started on line %d"), lineNumber(pInfo)); 772} 773 774// pInfo should be set to the first character after "<?" 775static void skipXMLProcessingInstruction(_CFXMLPlistParseInfo *pInfo) { 776 const char *begin = pInfo->curr, *end = pInfo->end - 2; // Looking for "?>" so we need at least 2 characters 777 while (pInfo->curr < end) { 778 if (*(pInfo->curr) == '?' && *(pInfo->curr+1) == '>') { 779 pInfo->curr += 2; 780 return; 781 } 782 pInfo->curr ++; 783 } 784 pInfo->curr = begin; 785 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing the processing instruction begun on line %d"), lineNumber(pInfo)); 786} 787 788// first character should be immediately after the "<!" 789static void skipDTD(_CFXMLPlistParseInfo *pInfo) { 790 // First pass "DOCTYPE" 791 if (pInfo->end - pInfo->curr < DOCTYPE_TAG_LENGTH || memcmp(pInfo->curr, CFXMLPlistTags[DOCTYPE_IX], DOCTYPE_TAG_LENGTH)) { 792 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Malformed DTD on line %d"), lineNumber(pInfo)); 793 return; 794 } 795 pInfo->curr += DOCTYPE_TAG_LENGTH; 796 skipWhitespace(pInfo); 797 798 // Look for either the beginning of a complex DTD or the end of the DOCTYPE structure 799 while (pInfo->curr < pInfo->end) { 800 char ch = *(pInfo->curr); 801 if (ch == '[') break; // inline DTD 802 if (ch == '>') { // End of the DTD 803 pInfo->curr ++; 804 return; 805 } 806 pInfo->curr ++; 807 } 808 if (pInfo->curr == pInfo->end) { 809 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing DTD")); 810 return; 811 } 812 813 // *Sigh* Must parse in-line DTD 814 skipInlineDTD(pInfo); 815 if (pInfo->error) return; 816 skipWhitespace(pInfo); 817 if (pInfo->error) return; 818 if (pInfo->curr < pInfo->end) { 819 if (*(pInfo->curr) == '>') { 820 pInfo->curr ++; 821 } else { 822 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while parsing DTD"), *(pInfo->curr), lineNumber(pInfo)); 823 } 824 } else { 825 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing DTD")); 826 } 827} 828 829static void skipPERef(_CFXMLPlistParseInfo *pInfo) { 830 const char *p = pInfo->curr; 831 while (p < pInfo->end) { 832 if (*p == ';') { 833 pInfo->curr = p+1; 834 return; 835 } 836 p ++; 837 } 838 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing percent-escape sequence begun on line %d"), lineNumber(pInfo)); 839} 840 841// First character should be just past '[' 842static void skipInlineDTD(_CFXMLPlistParseInfo *pInfo) { 843 while (!pInfo->error && pInfo->curr < pInfo->end) { 844 UniChar ch; 845 skipWhitespace(pInfo); 846 ch = *pInfo->curr; 847 if (ch == '%') { 848 pInfo->curr ++; 849 skipPERef(pInfo); 850 } else if (ch == '<') { 851 pInfo->curr ++; 852 if (pInfo->curr >= pInfo->end) { 853 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD")); 854 return; 855 } 856 ch = *(pInfo->curr); 857 if (ch == '?') { 858 pInfo->curr ++; 859 skipXMLProcessingInstruction(pInfo); 860 } else if (ch == '!') { 861 if (pInfo->curr + 2 < pInfo->end && (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-')) { 862 pInfo->curr += 3; 863 skipXMLComment(pInfo); 864 } else { 865 // Skip the myriad of DTD declarations of the form "<!string" ... ">" 866 pInfo->curr ++; // Past both '<' and '!' 867 while (pInfo->curr < pInfo->end) { 868 if (*(pInfo->curr) == '>') break; 869 pInfo->curr ++; 870 } 871 if (*(pInfo->curr) != '>') { 872 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD")); 873 return; 874 } 875 pInfo->curr ++; 876 } 877 } else { 878 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo)); 879 return; 880 } 881 } else if (ch == ']') { 882 pInfo->curr ++; 883 return; 884 } else { 885 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while parsing inline DTD"), ch, lineNumber(pInfo)); 886 return; 887 } 888 } 889 if (!pInfo->error) { 890 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF while parsing inline DTD")); 891 } 892} 893 894// content ::== (element | CharData | Reference | CDSect | PI | Comment)* 895// In the context of a plist, CharData, Reference and CDSect are not legal (they all resolve to strings). Skipping whitespace, then, the next character should be '<'. From there, we figure out which of the three remaining cases we have (element, PI, or Comment). 896static Boolean getContentObject(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out) { 897 if (isKey) *isKey = false; 898 while (!pInfo->error && pInfo->curr < pInfo->end) { 899 skipWhitespace(pInfo); 900 if (pInfo->curr >= pInfo->end) { 901 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 902 return false; 903 } 904 if (*(pInfo->curr) != '<') { 905 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while looking for open tag"), *(pInfo->curr), lineNumber(pInfo)); 906 return false; 907 } 908 pInfo->curr ++; 909 if (pInfo->curr >= pInfo->end) { 910 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 911 return false; 912 } 913 switch (*(pInfo->curr)) { 914 case '?': 915 // Processing instruction 916 skipXMLProcessingInstruction(pInfo); 917 break; 918 case '!': 919 // Could be a comment 920 if (pInfo->curr+2 >= pInfo->end) { 921 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 922 return false; 923 } 924 if (*(pInfo->curr+1) == '-' && *(pInfo->curr+2) == '-') { 925 pInfo->curr += 2; 926 skipXMLComment(pInfo); 927 } else { 928 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 929 return false; 930 } 931 break; 932 case '/': 933 // Whoops! Looks like we got to the end tag for the element whose content we're parsing 934 pInfo->curr --; // Back off to the '<' 935 return false; 936 default: 937 // Should be an element 938 return parseXMLElement(pInfo, isKey, out); 939 } 940 } 941 // Do not set the error string here; if it wasn't already set by one of the recursive parsing calls, the caller will quickly detect the failure (b/c pInfo->curr >= pInfo->end) and provide a more useful one of the form "end tag for <blah> not found" 942 return false; 943} 944 945static void parseCDSect_pl(_CFXMLPlistParseInfo *pInfo, CFMutableDataRef stringData) { 946 const char *end, *begin; 947 if (pInfo->end - pInfo->curr < CDSECT_TAG_LENGTH) { 948 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 949 return; 950 } 951 if (memcmp(pInfo->curr, CFXMLPlistTags[CDSECT_IX], CDSECT_TAG_LENGTH)) { 952 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered improper CDATA opening at line %d"), lineNumber(pInfo)); 953 return; 954 } 955 pInfo->curr += CDSECT_TAG_LENGTH; 956 begin = pInfo->curr; // Marks the first character of the CDATA content 957 end = pInfo->end-2; // So we can safely look 2 characters beyond p 958 while (pInfo->curr < end) { 959 if (*(pInfo->curr) == ']' && *(pInfo->curr+1) == ']' && *(pInfo->curr+2) == '>') { 960 // Found the end! 961 CFDataAppendBytes(stringData, (const UInt8 *)begin, pInfo->curr-begin); 962 pInfo->curr += 3; 963 return; 964 } 965 pInfo->curr ++; 966 } 967 // Never found the end mark 968 pInfo->curr = begin; 969 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not find end of CDATA started on line %d"), lineNumber(pInfo)); 970} 971 972// Only legal references are {lt, gt, amp, apos, quote, #ddd, #xAAA} 973static void parseEntityReference_pl(_CFXMLPlistParseInfo *pInfo, CFMutableDataRef stringData) { 974 int len; 975 pInfo->curr ++; // move past the '&'; 976 len = pInfo->end - pInfo->curr; // how many bytes we can safely scan 977 if (len < 1) { 978 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 979 return; 980 } 981 982 char ch; 983 switch (*(pInfo->curr)) { 984 case 'l': // "lt" 985 if (len >= 3 && *(pInfo->curr+1) == 't' && *(pInfo->curr+2) == ';') { 986 ch = '<'; 987 pInfo->curr += 3; 988 break; 989 } 990 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); 991 return; 992 case 'g': // "gt" 993 if (len >= 3 && *(pInfo->curr+1) == 't' && *(pInfo->curr+2) == ';') { 994 ch = '>'; 995 pInfo->curr += 3; 996 break; 997 } 998 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); 999 return; 1000 case 'a': // "apos" or "amp" 1001 if (len < 4) { // Not enough characters for either conversion 1002 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 1003 return; 1004 } 1005 if (*(pInfo->curr+1) == 'm') { 1006 // "amp" 1007 if (*(pInfo->curr+2) == 'p' && *(pInfo->curr+3) == ';') { 1008 ch = '&'; 1009 pInfo->curr += 4; 1010 break; 1011 } 1012 } else if (*(pInfo->curr+1) == 'p') { 1013 // "apos" 1014 if (len > 4 && *(pInfo->curr+2) == 'o' && *(pInfo->curr+3) == 's' && *(pInfo->curr+4) == ';') { 1015 ch = '\''; 1016 pInfo->curr += 5; 1017 break; 1018 } 1019 } 1020 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); 1021 return; 1022 case 'q': // "quote" 1023 if (len >= 5 && *(pInfo->curr+1) == 'u' && *(pInfo->curr+2) == 'o' && *(pInfo->curr+3) == 't' && *(pInfo->curr+4) == ';') { 1024 ch = '\"'; 1025 pInfo->curr += 5; 1026 break; 1027 } 1028 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); 1029 return; 1030 case '#': 1031 { 1032 uint16_t num = 0; 1033 Boolean isHex = false; 1034 if ( len < 4) { // Not enough characters to make it all fit! Need at least "&#d;" 1035 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 1036 return; 1037 } 1038 pInfo->curr ++; 1039 if (*(pInfo->curr) == 'x') { 1040 isHex = true; 1041 pInfo->curr ++; 1042 } 1043 while (pInfo->curr < pInfo->end) { 1044 ch = *(pInfo->curr); 1045 pInfo->curr ++; 1046 if (ch == ';') { 1047 // The value in num always refers to the unicode code point. We'll have to convert since the calling function expects UTF8 data. 1048 CFStringRef oneChar = CFStringCreateWithBytes(pInfo->allocator, (const uint8_t *)&num, 2, kCFStringEncodingUnicode, NO); 1049 uint8_t tmpBuf[6]; // max of 6 bytes for UTF8 1050 CFIndex tmpBufLength = 0; 1051 CFStringGetBytes(oneChar, CFRangeMake(0, 1), kCFStringEncodingUTF8, 0, NO, tmpBuf, 6, &tmpBufLength); 1052 CFDataAppendBytes(stringData, tmpBuf, tmpBufLength); 1053 __CFPListRelease(oneChar, pInfo->allocator); 1054 return; 1055 } 1056 if (!isHex) num = num*10; 1057 else num = num << 4; 1058 if (ch <= '9' && ch >= '0') { 1059 num += (ch - '0'); 1060 } else if (!isHex) { 1061 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch, lineNumber(pInfo)); 1062 return; 1063 } else if (ch >= 'a' && ch <= 'f') { 1064 num += 10 + (ch - 'a'); 1065 } else if (ch >= 'A' && ch <= 'F') { 1066 num += 10 + (ch - 'A'); 1067 } else { 1068 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c at line %d while parsing data"), ch, lineNumber(pInfo)); 1069 return; 1070 } 1071 } 1072 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 1073 return; 1074 } 1075 default: 1076 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown ampersand-escape sequence at line %d"), lineNumber(pInfo)); 1077 return; 1078 } 1079 CFDataAppendBytes(stringData, (const UInt8 *)&ch, 1); 1080} 1081 1082static void _createStringMap(_CFXMLPlistParseInfo *pInfo) { 1083 pInfo->stringTrie = CFBurstTrieCreate(); 1084 pInfo->stringCache = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks); 1085} 1086 1087static void _cleanupStringMap(_CFXMLPlistParseInfo *pInfo) { 1088 CFBurstTrieRelease(pInfo->stringTrie); 1089 CFRelease(pInfo->stringCache); 1090 pInfo->stringTrie = NULL; 1091 pInfo->stringCache = NULL; 1092} 1093 1094static CFStringRef _createUniqueStringWithUTF8Bytes(_CFXMLPlistParseInfo *pInfo, const char *base, CFIndex length) { 1095 if (length == 0) return !(0) ? (CFStringRef)CFRetain(CFSTR("")) : CFSTR(""); 1096 1097 CFStringRef result = NULL; 1098 uint32_t payload = 0; 1099 Boolean uniqued = CFBurstTrieContainsUTF8String(pInfo->stringTrie, (UInt8 *)base, length, &payload); 1100 if (uniqued && payload > 0) { 1101 // The index is at payload - 1 (see below). 1102 result = (CFStringRef)CFArrayGetValueAtIndex(pInfo->stringCache, (CFIndex)payload - 1); 1103 CFRetain(result); 1104 } else { 1105 result = CFStringCreateWithBytes(pInfo->allocator, (const UInt8 *)base, length, kCFStringEncodingUTF8, NO); 1106 if (!result) return NULL; 1107 // Payload must be >0, so the actual index of the value is at payload - 1 1108 // We also get add to the array after we make sure that CFBurstTrieAddUTF8String succeeds (it can fail, if the string is too large, for example) 1109 payload = CFArrayGetCount(pInfo->stringCache) + 1; 1110 Boolean didAddToBurstTrie = CFBurstTrieAddUTF8String(pInfo->stringTrie, (UInt8 *)base, length, payload); 1111 if (didAddToBurstTrie) { 1112 CFArrayAppendValue(pInfo->stringCache, result); 1113 } 1114 } 1115 return result; 1116} 1117 1118// String could be comprised of characters, CDSects, or references to one of the "well-known" entities ('<', '>', '&', ''', '"') 1119static Boolean parseStringTag(_CFXMLPlistParseInfo *pInfo, CFStringRef *out) { 1120 const char *mark = pInfo->curr; 1121 CFMutableDataRef stringData = NULL; 1122 while (!pInfo->error && pInfo->curr < pInfo->end) { 1123 char ch = *(pInfo->curr); 1124 if (ch == '<') { 1125 if (pInfo->curr + 1 >= pInfo->end) break; 1126 // Could be a CDSect; could be the end of the string 1127 if (*(pInfo->curr+1) != '!') break; // End of the string 1128 if (!stringData) stringData = CFDataCreateMutable(pInfo->allocator, 0); 1129 CFDataAppendBytes(stringData, (const UInt8 *)mark, pInfo->curr - mark); 1130 parseCDSect_pl(pInfo, stringData); // TODO: move to return boolean 1131 mark = pInfo->curr; 1132 } else if (ch == '&') { 1133 if (!stringData) stringData = CFDataCreateMutable(pInfo->allocator, 0); 1134 CFDataAppendBytes(stringData, (const UInt8 *)mark, pInfo->curr - mark); 1135 parseEntityReference_pl(pInfo, stringData); // TODO: move to return boolean 1136 mark = pInfo->curr; 1137 } else { 1138 pInfo->curr ++; 1139 } 1140 } 1141 1142 if (pInfo->error) { 1143 __CFPListRelease(stringData, pInfo->allocator); 1144 return false; 1145 } 1146 1147 if (!stringData) { 1148 if (pInfo->skip) { 1149 *out = NULL; 1150 } else { 1151 if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) { 1152 CFStringRef s = _createUniqueStringWithUTF8Bytes(pInfo, mark, pInfo->curr - mark); 1153 if (!s) { 1154 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unable to convert string to correct encoding")); 1155 return false; 1156 } 1157 *out = s; 1158 } else { 1159 CFStringRef s = CFStringCreateWithBytes(pInfo->allocator, (const UInt8 *)mark, pInfo->curr - mark, kCFStringEncodingUTF8, NO); 1160 if (!s) { 1161 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unable to convert string to correct encoding")); 1162 return false; 1163 } 1164 *out = CFStringCreateMutableCopy(pInfo->allocator, 0, s); 1165 __CFPListRelease(s, pInfo->allocator); 1166 } 1167 } 1168 return true; 1169 } else { 1170 if (pInfo->skip) { 1171 *out = NULL; 1172 } else { 1173 CFDataAppendBytes(stringData, (const UInt8 *)mark, pInfo->curr - mark); 1174 if (pInfo->mutabilityOption != kCFPropertyListMutableContainersAndLeaves) { 1175 CFStringRef s = _createUniqueStringWithUTF8Bytes(pInfo, (const char *)CFDataGetBytePtr(stringData), CFDataGetLength(stringData)); 1176 if (!s) { 1177 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unable to convert string to correct encoding")); 1178 return false; 1179 } 1180 *out = s; 1181 } else { 1182 CFStringRef s = CFStringCreateWithBytes(pInfo->allocator, (const UInt8 *)CFDataGetBytePtr(stringData), CFDataGetLength(stringData), kCFStringEncodingUTF8, NO); 1183 if (!s) { 1184 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unable to convert string to correct encoding")); 1185 return false; 1186 } 1187 *out = CFStringCreateMutableCopy(pInfo->allocator, 0, s); 1188 __CFPListRelease(s, pInfo->allocator); 1189 } 1190 } 1191 __CFPListRelease(stringData, pInfo->allocator); 1192 return true; 1193 } 1194} 1195 1196static Boolean checkForCloseTag(_CFXMLPlistParseInfo *pInfo, const char *tag, CFIndex tagLen) { 1197 if (pInfo->end - pInfo->curr < tagLen + 3) { 1198 if (!pInfo->error) { 1199 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 1200 } 1201 return false; 1202 } 1203 if (*(pInfo->curr) != '<' || *(++pInfo->curr) != '/') { 1204 if (!pInfo->error) { 1205 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo->curr), lineNumber(pInfo)); 1206 } 1207 return false; 1208 } 1209 pInfo->curr ++; 1210 if (memcmp(pInfo->curr, tag, tagLen)) { 1211 CFStringRef str = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)tag, tagLen, kCFStringEncodingUTF8, NO); 1212 if (!pInfo->error) { 1213 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Close tag on line %d does not match open tag %@"), lineNumber(pInfo), str); 1214 } 1215 CFRelease(str); 1216 return false; 1217 } 1218 pInfo->curr += tagLen; 1219 skipWhitespace(pInfo); 1220 if (pInfo->curr == pInfo->end) { 1221 if (!pInfo->error) { 1222 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 1223 } 1224 return false; 1225 } 1226 if (*(pInfo->curr) != '>') { 1227 if (!pInfo->error) { 1228 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected character %c on line %d while looking for close tag"), *(pInfo->curr), lineNumber(pInfo)); 1229 } 1230 return false; 1231 } 1232 pInfo->curr ++; 1233 return true; 1234} 1235 1236// pInfo should be set to the first content character of the <plist> 1237static Boolean parsePListTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { 1238 CFTypeRef result = NULL; 1239 if (!getContentObject(pInfo, NULL, &result)) { 1240 if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty plist tag")); 1241 return false; 1242 } 1243 const char *save = pInfo->curr; // Save this in case the next step fails 1244 CFTypeRef tmp = NULL; 1245 if (getContentObject(pInfo, NULL, &tmp)) { 1246 // Got an extra object 1247 __CFPListRelease(tmp, pInfo->allocator); 1248 __CFPListRelease(result, pInfo->allocator); 1249 pInfo->curr = save; 1250 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected element at line %d (plist can only include one object)"), lineNumber(pInfo)); 1251 return false; 1252 } 1253 if (pInfo->error) { 1254 // Parse failed catastrophically 1255 __CFPListRelease(result, pInfo->allocator); 1256 return false; 1257 } 1258 if (!checkForCloseTag(pInfo, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH)) { 1259 __CFPListRelease(result, pInfo->allocator); 1260 return false; 1261 } 1262 *out = result; 1263 return true; 1264} 1265 1266static int allowImmutableCollections = -1; 1267 1268static void checkImmutableCollections(void) { 1269 allowImmutableCollections = (NULL == __CFgetenv("CFPropertyListAllowImmutableCollections")) ? 0 : 1; 1270} 1271 1272// This converts the input value, a set of strings, into the form that's efficient for using during recursive decent parsing, a set of arrays 1273static CFSetRef createTopLevelKeypaths(CFAllocatorRef allocator, CFSetRef keyPaths) { 1274 if (!keyPaths) return NULL; 1275 1276 CFIndex count = CFSetGetCount(keyPaths); 1277 new_cftype_array(keyPathValues, count); 1278 CFSetGetValues(keyPaths, keyPathValues); 1279 CFMutableSetRef splitKeyPathSet = CFSetCreateMutable(allocator, count, &kCFTypeSetCallBacks); 1280 for (CFIndex i = 0; i < count; i++) { 1281 // Split each key path and add it to the split path set, which we will reference throughout parsing 1282 CFArrayRef split = CFStringCreateArrayBySeparatingStrings(allocator, (CFStringRef)(keyPathValues[i]), CFSTR(":")); 1283 CFSetAddValue(splitKeyPathSet, split); 1284 __CFPListRelease(split, allocator); 1285 } 1286 free_cftype_array(keyPathValues); 1287 return splitKeyPathSet; 1288} 1289 1290// This splits up the keypaths into the ones relevant for this level (of array or dictionary), and the ones for the next level (of array or dictionary) 1291CF_PRIVATE void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator, CFSetRef currentKeys, CFSetRef *theseKeys, CFSetRef *nextKeys) { 1292 if (!currentKeys) { *theseKeys = NULL; *nextKeys = NULL; return; } 1293 1294 CFIndex count = CFSetGetCount(currentKeys); 1295 1296 // For each array in the current key path set, grab the item at the start of the list and put it into theseKeys. The rest of the array goes into nextKeys. 1297 CFMutableSetRef outTheseKeys = NULL; 1298 CFMutableSetRef outNextKeys = NULL; 1299 1300 new_cftype_array(currentKeyPaths, count); 1301 CFSetGetValues(currentKeys, currentKeyPaths); 1302 for (CFIndex i = 0; i < count; i++) { 1303 CFArrayRef oneKeyPath = (CFArrayRef)currentKeyPaths[i]; 1304 CFIndex keyPathCount = CFArrayGetCount(oneKeyPath); 1305 1306 if (keyPathCount > 0) { 1307 if (!outTheseKeys) outTheseKeys = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks); 1308 1309 CFSetAddValue(outTheseKeys, CFArrayGetValueAtIndex(oneKeyPath, 0)); 1310 } 1311 1312 if (keyPathCount > 1) { 1313 if (!outNextKeys) outNextKeys = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks); 1314 1315 // Create an array with values from 1 - end of list 1316 new_cftype_array(restOfKeys, keyPathCount - 1); 1317 CFArrayGetValues(oneKeyPath, CFRangeMake(1, CFArrayGetCount(oneKeyPath) - 1), restOfKeys); 1318 CFArrayRef newNextKeys = CFArrayCreate(allocator, restOfKeys, CFArrayGetCount(oneKeyPath) - 1, &kCFTypeArrayCallBacks); 1319 CFSetAddValue(outNextKeys, newNextKeys); 1320 __CFPListRelease(newNextKeys, allocator); 1321 free_cftype_array(restOfKeys); 1322 1323 } 1324 } 1325 1326 *theseKeys = outTheseKeys; 1327 *nextKeys = outNextKeys; 1328} 1329 1330static Boolean parseArrayTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { 1331 CFTypeRef tmp = NULL; 1332 1333 if (pInfo->skip) { 1334 Boolean result = getContentObject(pInfo, NULL, &tmp); 1335 while (result) { 1336 if (tmp) { 1337 // Shouldn't happen (if skipping, all content values should be null), but just in case 1338 __CFPListRelease(tmp, pInfo->allocator); 1339 } 1340 result = getContentObject(pInfo, NULL, &tmp); 1341 } 1342 1343 if (pInfo->error) { 1344 // getContentObject encountered a parse error 1345 return false; 1346 } 1347 if (!checkForCloseTag(pInfo, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH)) { 1348 return false; 1349 } else { 1350 *out = NULL; 1351 return true; 1352 } 1353 } 1354 1355 CFMutableArrayRef array = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks); 1356 Boolean result; 1357 1358 CFIndex count = 0; 1359 CFSetRef oldKeyPaths = pInfo->keyPaths; 1360 CFSetRef newKeyPaths, keys; 1361 __CFPropertyListCreateSplitKeypaths(pInfo->allocator, pInfo->keyPaths, &keys, &newKeyPaths); 1362 1363 if (keys) { 1364 CFStringRef countString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("%ld"), count); 1365 if (!CFSetContainsValue(keys, countString)) pInfo->skip = true; 1366 __CFPListRelease(countString, pInfo->allocator); 1367 count++; 1368 pInfo->keyPaths = newKeyPaths; 1369 } 1370 result = getContentObject(pInfo, NULL, &tmp); 1371 if (keys) { 1372 pInfo->keyPaths = oldKeyPaths; 1373 pInfo->skip = false; 1374 } 1375 1376 while (result) { 1377 if (tmp) { 1378 CFArrayAppendValue(array, tmp); 1379 __CFPListRelease(tmp, pInfo->allocator); 1380 } 1381 1382 if (keys) { 1383 // prep for getting next object 1384 CFStringRef countString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("%ld"), count); 1385 if (!CFSetContainsValue(keys, countString)) pInfo->skip = true; 1386 __CFPListRelease(countString, pInfo->allocator); 1387 count++; 1388 pInfo->keyPaths = newKeyPaths; 1389 } 1390 result = getContentObject(pInfo, NULL, &tmp); 1391 if (keys) { 1392 // reset after getting object 1393 pInfo->keyPaths = oldKeyPaths; 1394 pInfo->skip = false; 1395 } 1396 1397 } 1398 1399 __CFPListRelease(newKeyPaths, pInfo->allocator); 1400 __CFPListRelease(keys, pInfo->allocator); 1401 1402 if (pInfo->error) { // getContentObject encountered a parse error 1403 __CFPListRelease(array, pInfo->allocator); 1404 return false; 1405 } 1406 if (!checkForCloseTag(pInfo, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH)) { 1407 __CFPListRelease(array, pInfo->allocator); 1408 return false; 1409 } 1410 if (-1 == allowImmutableCollections) { 1411 checkImmutableCollections(); 1412 } 1413 if (1 == allowImmutableCollections) { 1414 if (pInfo->mutabilityOption == kCFPropertyListImmutable) { 1415 CFArrayRef newArray = CFArrayCreateCopy(pInfo->allocator, array); 1416 __CFPListRelease(array, pInfo->allocator); 1417 array = (CFMutableArrayRef)newArray; 1418 } 1419 } 1420 *out = array; 1421 return true; 1422} 1423 1424static Boolean parseDictTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { 1425 Boolean gotKey; 1426 Boolean result; 1427 CFTypeRef key = NULL, value = NULL; 1428 1429 if (pInfo->skip) { 1430 result = getContentObject(pInfo, &gotKey, &key); 1431 while (result) { 1432 if (!gotKey) { 1433 if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo)); 1434 return false; 1435 } 1436 result = getContentObject(pInfo, NULL, &value); 1437 if (!result) { 1438 if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo)); 1439 return false; 1440 } 1441 // key and value should be null, but we'll release just in case here 1442 __CFPListRelease(key, pInfo->allocator); 1443 key = NULL; 1444 __CFPListRelease(value, pInfo->allocator); 1445 value = NULL; 1446 result = getContentObject(pInfo, &gotKey, &key); 1447 } 1448 if (checkForCloseTag(pInfo, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) { 1449 *out = NULL; 1450 return true; 1451 } else { 1452 return false; 1453 } 1454 } 1455 1456 CFSetRef oldKeyPaths = pInfo->keyPaths; 1457 CFSetRef nextKeyPaths, theseKeyPaths; 1458 __CFPropertyListCreateSplitKeypaths(pInfo->allocator, pInfo->keyPaths, &theseKeyPaths, &nextKeyPaths); 1459 1460 CFMutableDictionaryRef dict = NULL; 1461 1462 result = getContentObject(pInfo, &gotKey, &key); 1463 while (result && key) { 1464 if (!gotKey) { 1465 if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Found non-key inside <dict> at line %d"), lineNumber(pInfo)); 1466 __CFPListRelease(key, pInfo->allocator); 1467 __CFPListRelease(nextKeyPaths, pInfo->allocator); 1468 __CFPListRelease(theseKeyPaths, pInfo->allocator); 1469 __CFPListRelease(dict, pInfo->allocator); 1470 return false; 1471 } 1472 1473 if (theseKeyPaths) { 1474 if (!CFSetContainsValue(theseKeyPaths, key)) pInfo->skip = true; 1475 pInfo->keyPaths = nextKeyPaths; 1476 } 1477 result = getContentObject(pInfo, NULL, &value); 1478 if (theseKeyPaths) { 1479 pInfo->keyPaths = oldKeyPaths; 1480 pInfo->skip = false; 1481 } 1482 1483 if (!result) { 1484 if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Value missing for key inside <dict> at line %d"), lineNumber(pInfo)); 1485 __CFPListRelease(key, pInfo->allocator); 1486 __CFPListRelease(nextKeyPaths, pInfo->allocator); 1487 __CFPListRelease(theseKeyPaths, pInfo->allocator); 1488 __CFPListRelease(dict, pInfo->allocator); 1489 return false; 1490 } 1491 1492 if (key && value) { 1493 if (NULL == dict) { 1494 dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1495 _CFDictionarySetCapacity(dict, 10); 1496 } 1497 CFDictionarySetValue(dict, key, value); 1498 } 1499 1500 __CFPListRelease(key, pInfo->allocator); 1501 key = NULL; 1502 __CFPListRelease(value, pInfo->allocator); 1503 value = NULL; 1504 1505 result = getContentObject(pInfo, &gotKey, &key); 1506 } 1507 1508 __CFPListRelease(nextKeyPaths, pInfo->allocator); 1509 __CFPListRelease(theseKeyPaths, pInfo->allocator); 1510 1511 if (checkForCloseTag(pInfo, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) { 1512 if (NULL == dict) { 1513 if (pInfo->mutabilityOption == kCFPropertyListImmutable) { 1514 dict = (CFMutableDictionaryRef)CFDictionaryCreate(pInfo->allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1515 } else { 1516 dict = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1517 } 1518 } else { 1519 CFIndex cnt = CFDictionaryGetCount(dict); 1520 if (1 == cnt) { 1521 CFTypeRef val = CFDictionaryGetValue(dict, CFSTR("CF$UID")); 1522 if (val && CFGetTypeID(val) == numbertype) { 1523 CFTypeRef uid; 1524 uint32_t v; 1525 CFNumberGetValue((CFNumberRef)val, kCFNumberSInt32Type, &v); 1526 uid = (CFTypeRef)_CFKeyedArchiverUIDCreate(pInfo->allocator, v); 1527 __CFPListRelease(dict, pInfo->allocator); 1528 *out = uid; 1529 return true; 1530 } 1531 } 1532 if (-1 == allowImmutableCollections) checkImmutableCollections(); 1533 if (1 == allowImmutableCollections) { 1534 if (pInfo->mutabilityOption == kCFPropertyListImmutable) { 1535 CFDictionaryRef newDict = CFDictionaryCreateCopy(pInfo->allocator, dict); 1536 __CFPListRelease(dict, pInfo->allocator); 1537 dict = (CFMutableDictionaryRef)newDict; 1538 } 1539 } 1540 } 1541 *out = dict; 1542 return true; 1543 } 1544 1545 if (dict) __CFPListRelease(dict, pInfo->allocator); 1546 return false; 1547} 1548 1549static Boolean parseDataTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { 1550 const char *base = pInfo->curr; 1551 static const signed char dataDecodeTable[128] = { 1552 /* 000 */ -1, -1, -1, -1, -1, -1, -1, -1, 1553 /* 010 */ -1, -1, -1, -1, -1, -1, -1, -1, 1554 /* 020 */ -1, -1, -1, -1, -1, -1, -1, -1, 1555 /* 030 */ -1, -1, -1, -1, -1, -1, -1, -1, 1556 /* ' ' */ -1, -1, -1, -1, -1, -1, -1, -1, 1557 /* '(' */ -1, -1, -1, 62, -1, -1, -1, 63, 1558 /* '0' */ 52, 53, 54, 55, 56, 57, 58, 59, 1559 /* '8' */ 60, 61, -1, -1, -1, 0, -1, -1, 1560 /* '@' */ -1, 0, 1, 2, 3, 4, 5, 6, 1561 /* 'H' */ 7, 8, 9, 10, 11, 12, 13, 14, 1562 /* 'P' */ 15, 16, 17, 18, 19, 20, 21, 22, 1563 /* 'X' */ 23, 24, 25, -1, -1, -1, -1, -1, 1564 /* '`' */ -1, 26, 27, 28, 29, 30, 31, 32, 1565 /* 'h' */ 33, 34, 35, 36, 37, 38, 39, 40, 1566 /* 'p' */ 41, 42, 43, 44, 45, 46, 47, 48, 1567 /* 'x' */ 49, 50, 51, -1, -1, -1, -1, -1 1568 }; 1569 1570 int tmpbufpos = 0; 1571 int tmpbuflen = 256; 1572 uint8_t *tmpbuf = pInfo->skip ? NULL : (uint8_t *)CFAllocatorAllocate(pInfo->allocator, tmpbuflen, 0); 1573 int numeq = 0; 1574 int acc = 0; 1575 int cntr = 0; 1576 1577 for (; pInfo->curr < pInfo->end; pInfo->curr++) { 1578 signed char c = *(pInfo->curr); 1579 if (c == '<') { 1580 break; 1581 } 1582 if ('=' == c) { 1583 numeq++; 1584 } else if (!isspace(c)) { 1585 numeq = 0; 1586 } 1587 if (dataDecodeTable[c] < 0) 1588 continue; 1589 cntr++; 1590 acc <<= 6; 1591 acc += dataDecodeTable[c]; 1592 if (!pInfo->skip && 0 == (cntr & 0x3)) { 1593 if (tmpbuflen <= tmpbufpos + 2) { 1594 if (tmpbuflen < 256 * 1024) { 1595 tmpbuflen *= 4; 1596 } else if (tmpbuflen < 16 * 1024 * 1024) { 1597 tmpbuflen *= 2; 1598 } else { 1599 // once in this stage, this will be really slow 1600 // and really potentially fragment memory 1601 tmpbuflen += 256 * 1024; 1602 } 1603 tmpbuf = (uint8_t *)CFAllocatorReallocate(pInfo->allocator, tmpbuf, tmpbuflen, 0); 1604 if (!tmpbuf) HALT; // out of memory 1605 } 1606 tmpbuf[tmpbufpos++] = (acc >> 16) & 0xff; 1607 if (numeq < 2) tmpbuf[tmpbufpos++] = (acc >> 8) & 0xff; 1608 if (numeq < 1) tmpbuf[tmpbufpos++] = acc & 0xff; 1609 } 1610 } 1611 1612 CFDataRef result = NULL; 1613 if (!pInfo->skip) { 1614 if (pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { 1615 result = (CFDataRef)CFDataCreateMutable(pInfo->allocator, 0); 1616 CFDataAppendBytes((CFMutableDataRef)result, tmpbuf, tmpbufpos); 1617 CFAllocatorDeallocate(pInfo->allocator, tmpbuf); 1618 } else { 1619 result = CFDataCreateWithBytesNoCopy(pInfo->allocator, tmpbuf, tmpbufpos, pInfo->allocator); 1620 } 1621 if (!result) { 1622 pInfo->curr = base; 1623 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not interpret <data> at line %d (should be base64-encoded)"), lineNumber(pInfo)); 1624 return false; 1625 } 1626 } 1627 1628 if (checkForCloseTag(pInfo, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH)) { 1629 *out = result; 1630 return true; 1631 } else { 1632 __CFPListRelease(result, pInfo->allocator); 1633 return false; 1634 } 1635} 1636 1637CF_INLINE Boolean read2DigitNumber(_CFXMLPlistParseInfo *pInfo, int32_t *result) { 1638 char ch1, ch2; 1639 if (pInfo->curr + 2 >= pInfo->end) { 1640 return false; 1641 } 1642 ch1 = *pInfo->curr; 1643 ch2 = *(pInfo->curr + 1); 1644 pInfo->curr += 2; 1645 if (!isdigit(ch1) || !isdigit(ch2)) { 1646 return false; 1647 } 1648 *result = (ch1 - '0')*10 + (ch2 - '0'); 1649 return true; 1650} 1651 1652// YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z' 1653static Boolean parseDateTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { 1654 int32_t year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; 1655 int32_t num = 0; 1656 Boolean badForm = false; 1657 Boolean yearIsNegative = false; 1658 1659 if (pInfo->curr < pInfo->end && *pInfo->curr == '-') { 1660 yearIsNegative = true; 1661 pInfo->curr++; 1662 } 1663 1664 while (pInfo->curr < pInfo->end && isdigit(*pInfo->curr)) { 1665 year = 10*year + (*pInfo->curr) - '0'; 1666 pInfo->curr ++; 1667 } 1668 if (pInfo->curr >= pInfo->end || *pInfo->curr != '-') { 1669 badForm = true; 1670 } else { 1671 pInfo->curr ++; 1672 } 1673 1674 if (!badForm && read2DigitNumber(pInfo, &month) && pInfo->curr < pInfo->end && *pInfo->curr == '-') { 1675 pInfo->curr ++; 1676 } else { 1677 badForm = true; 1678 } 1679 1680 if (!badForm && read2DigitNumber(pInfo, &day) && pInfo->curr < pInfo->end && *pInfo->curr == 'T') { 1681 pInfo->curr ++; 1682 } else { 1683 badForm = true; 1684 } 1685 1686 if (!badForm && read2DigitNumber(pInfo, &hour) && pInfo->curr < pInfo->end && *pInfo->curr == ':') { 1687 pInfo->curr ++; 1688 } else { 1689 badForm = true; 1690 } 1691 1692 if (!badForm && read2DigitNumber(pInfo, &minute) && pInfo->curr < pInfo->end && *pInfo->curr == ':') { 1693 pInfo->curr ++; 1694 } else { 1695 badForm = true; 1696 } 1697 1698 if (!badForm && read2DigitNumber(pInfo, &num) && pInfo->curr < pInfo->end && *pInfo->curr == 'Z') { 1699 second = num; 1700 pInfo->curr ++; 1701 } else { 1702 badForm = true; 1703 } 1704 1705 if (badForm || !checkForCloseTag(pInfo, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH)) { 1706 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not interpret <date> at line %d"), lineNumber(pInfo)); 1707 return false; 1708 } 1709 1710 CFAbsoluteTime at = 0.0; 1711#if 1 1712 CFGregorianDate date = {yearIsNegative ? -year : year, month, day, hour, minute, second}; 1713 at = CFGregorianDateGetAbsoluteTime(date, NULL); 1714#else 1715 // this doesn't work 1716 CFCalendarRef calendar = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFCalendarIdentifierGregorian); 1717 CFTimeZoneRef tz = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, CFSTR("GMT"), true); 1718 CFCalendarSetTimeZone(calendar, tz); 1719 CFCalendarComposeAbsoluteTime(calendar, &at, (const uint8_t *)"yMdHms", year, month, day, hour, minute, second); 1720 CFRelease(calendar); 1721 CFRelease(tz); 1722#endif 1723 if (pInfo->skip) { 1724 *out = NULL; 1725 } else { 1726 *out = CFDateCreate(pInfo->allocator, at); 1727 } 1728 return true; 1729} 1730 1731static Boolean parseRealTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { 1732 CFStringRef str = NULL; 1733 if (!parseStringTag(pInfo, &str)) { 1734 if (!pInfo->error) pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo)); 1735 return false; 1736 } 1737 1738 CFNumberRef result = NULL; 1739 1740 if (!pInfo->skip) { 1741 if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("nan"), kCFCompareCaseInsensitive)) result = kCFNumberNaN; 1742 else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("+infinity"), kCFCompareCaseInsensitive)) result = kCFNumberPositiveInfinity; 1743 else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("-infinity"), kCFCompareCaseInsensitive)) result = kCFNumberNegativeInfinity; 1744 else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("infinity"), kCFCompareCaseInsensitive)) result = kCFNumberPositiveInfinity; 1745 else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("-inf"), kCFCompareCaseInsensitive)) result = kCFNumberNegativeInfinity; 1746 else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("inf"), kCFCompareCaseInsensitive)) result = kCFNumberPositiveInfinity; 1747 else if (kCFCompareEqualTo == CFStringCompare(str, CFSTR("+inf"), kCFCompareCaseInsensitive)) result = kCFNumberPositiveInfinity; 1748 1749 if (result) { 1750 CFRetain(result); 1751 } else { 1752 CFIndex len = CFStringGetLength(str); 1753 CFStringInlineBuffer buf; 1754 CFStringInitInlineBuffer(str, &buf, CFRangeMake(0, len)); 1755 SInt32 idx = 0; 1756 double val; 1757 if (!__CFStringScanDouble(&buf, NULL, &idx, &val) || idx != len) { 1758 __CFPListRelease(str, pInfo->allocator); 1759 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered misformatted real on line %d"), lineNumber(pInfo)); 1760 return false; 1761 } 1762 result = CFNumberCreate(pInfo->allocator, kCFNumberDoubleType, &val); 1763 } 1764 } 1765 1766 __CFPListRelease(str, pInfo->allocator); 1767 if (checkForCloseTag(pInfo, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) { 1768 *out = result; 1769 return true; 1770 } else { 1771 __CFPListRelease(result, pInfo->allocator); 1772 return false; 1773 } 1774} 1775 1776#define GET_CH if (pInfo->curr == pInfo->end) { \ 1777 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Premature end of file after <integer> on line %d"), lineNumber(pInfo)); \ 1778 return false; \ 1779 } \ 1780 ch = *(pInfo->curr) 1781 1782typedef struct { 1783 int64_t high; 1784 uint64_t low; 1785} CFSInt128Struct; 1786 1787enum { 1788 kCFNumberSInt128Type = 17 1789}; 1790 1791CF_INLINE bool isWhitespace(const char *utf8bytes, const char *end) { 1792 // Converted UTF-16 isWhitespace from CFString to UTF8 bytes to get full list of UTF8 whitespace 1793 /* 1794 0020 -> <20> 1795 0009 -> <09> 1796 00a0 -> <c2a0> 1797 1680 -> <e19a80> 1798 2000 -> <e28080> 1799 2001 -> <e28081> 1800 2002 -> <e28082> 1801 2003 -> <e28083> 1802 2004 -> <e28084> 1803 2005 -> <e28085> 1804 2006 -> <e28086> 1805 2007 -> <e28087> 1806 2008 -> <e28088> 1807 2009 -> <e28089> 1808 200a -> <e2808a> 1809 200b -> <e2808b> 1810 202f -> <e280af> 1811 205f -> <e2819f> 1812 3000 -> <e38080> 1813 */ 1814 // Except we consider some additional values from 0x0 to 0x21 and 0x7E to 0xA1 as whitespace, for compatability 1815 unsigned char byte1 = *utf8bytes; 1816 if (byte1 < 0x21 || (byte1 > 0x7E && byte1 < 0xA1)) return true; 1817 if ((byte1 == 0xe2 || byte1 == 0xe3) && (end - utf8bytes >= 3)) { 1818 // Check other possibilities in the 3-bytes range 1819 unsigned char byte2 = *(utf8bytes + 1); 1820 unsigned char byte3 = *(utf8bytes + 2); 1821 if (byte1 == 0xe2 && byte2 == 0x80) { 1822 return ((byte3 >= 80 && byte3 <= 0x8b) || byte3 == 0xaf); 1823 } else if (byte1 == 0xe2 && byte2 == 0x81) { 1824 return byte3 == 0x9f; 1825 } else if (byte1 == 0xe3 && byte2 == 0x80 && byte3 == 0x80) { 1826 return true; 1827 } 1828 } 1829 return false; 1830} 1831 1832static Boolean parseIntegerTag(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { 1833 bool isHex = false, isNeg = false, hadLeadingZero = false; 1834 char ch = 0; 1835 1836 // decimal_constant S*(-|+)?S*[0-9]+ (S == space) 1837 // hex_constant S*(-|+)?S*0[xX][0-9a-fA-F]+ (S == space) 1838 1839 while (pInfo->curr < pInfo->end && isWhitespace(pInfo->curr, pInfo->end)) pInfo->curr++; 1840 GET_CH; 1841 if ('<' == ch) { 1842 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo)); 1843 return false; 1844 } 1845 if ('-' == ch || '+' == ch) { 1846 isNeg = ('-' == ch); 1847 pInfo->curr++; 1848 while (pInfo->curr < pInfo->end && isWhitespace(pInfo->curr, pInfo->end)) pInfo->curr++; 1849 } 1850 GET_CH; 1851 if ('0' == ch) { 1852 if (pInfo->curr + 1 < pInfo->end && ('x' == *(pInfo->curr + 1) || 'X' == *(pInfo->curr + 1))) { 1853 pInfo->curr++; 1854 isHex = true; 1855 } else { 1856 hadLeadingZero = true; 1857 } 1858 pInfo->curr++; 1859 } 1860 GET_CH; 1861 while ('0' == ch) { 1862 hadLeadingZero = true; 1863 pInfo->curr++; 1864 GET_CH; 1865 } 1866 if ('<' == ch && hadLeadingZero) { // nothing but zeros 1867 int32_t val = 0; 1868 if (!checkForCloseTag(pInfo, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) { 1869 // checkForCloseTag() sets error string 1870 return false; 1871 } 1872 if (pInfo->skip) { 1873 *out = NULL; 1874 } else { 1875 *out = CFNumberCreate(pInfo->allocator, kCFNumberSInt32Type, &val); 1876 } 1877 return true; 1878 } 1879 if ('<' == ch) { 1880 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Incomplete <integer> on line %d"), lineNumber(pInfo)); 1881 return false; 1882 } 1883 uint64_t value = 0; 1884 uint32_t multiplier = (isHex ? 16 : 10); 1885 while ('<' != ch) { 1886 uint32_t new_digit = 0; 1887 switch (ch) { 1888 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': 1889 new_digit = (ch - '0'); 1890 break; 1891 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 1892 new_digit = (ch - 'a' + 10); 1893 break; 1894 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 1895 new_digit = (ch - 'A' + 10); 1896 break; 1897 default: // other character 1898 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unknown character '%c' (0x%x) in <integer> on line %d"), ch, ch, lineNumber(pInfo)); 1899 return false; 1900 } 1901 if (!isHex && new_digit > 9) { 1902 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Hex digit in non-hex <integer> on line %d"), lineNumber(pInfo)); 1903 return false; 1904 } 1905 if (UINT64_MAX / multiplier < value) { 1906 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo)); 1907 return false; 1908 } 1909 value = multiplier * value; 1910 if (UINT64_MAX - new_digit < value) { 1911 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Integer overflow in <integer> on line %d"), lineNumber(pInfo)); 1912 return false; 1913 } 1914 value = value + new_digit; 1915 if (isNeg && (uint64_t)INT64_MAX + 1 < value) { 1916 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Integer underflow in <integer> on line %d"), lineNumber(pInfo)); 1917 return false; 1918 } 1919 pInfo->curr++; 1920 GET_CH; 1921 } 1922 if (!checkForCloseTag(pInfo, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) { 1923 // checkForCloseTag() sets error string 1924 return false; 1925 } 1926 1927 if (pInfo->skip) { 1928 *out = NULL; 1929 } else { 1930 if (isNeg || value <= INT64_MAX) { 1931 int64_t v = value; 1932 if (isNeg) v = -v; // no-op if INT64_MIN 1933 *out = CFNumberCreate(pInfo->allocator, kCFNumberSInt64Type, &v); 1934 } else { 1935 CFSInt128Struct val; 1936 val.high = 0; 1937 val.low = value; 1938 *out = CFNumberCreate(pInfo->allocator, kCFNumberSInt128Type, &val); 1939 } 1940 } 1941 return true; 1942} 1943 1944#undef GET_CH 1945 1946// Returned object is retained; caller must free. pInfo->curr expected to point to the first character after the '<' 1947static Boolean parseXMLElement(_CFXMLPlistParseInfo *pInfo, Boolean *isKey, CFTypeRef *out) { 1948 const char *marker = pInfo->curr; 1949 int markerLength = -1; 1950 Boolean isEmpty; 1951 int markerIx = -1; 1952 1953 if (isKey) *isKey = false; 1954 while (pInfo->curr < pInfo->end) { 1955 char ch = *(pInfo->curr); 1956 if (ch == ' ' || ch == '\t' || ch == '\n' || ch =='\r') { 1957 if (markerLength == -1) markerLength = pInfo->curr - marker; 1958 } else if (ch == '>') { 1959 break; 1960 } 1961 pInfo->curr ++; 1962 } 1963 if (pInfo->curr >= pInfo->end) { 1964 return false; 1965 } 1966 isEmpty = (*(pInfo->curr-1) == '/'); 1967 if (markerLength == -1) 1968 markerLength = pInfo->curr - (isEmpty ? 1 : 0) - marker; 1969 pInfo->curr ++; // Advance past '>' 1970 if (markerLength == 0) { 1971 // Back up to the beginning of the marker 1972 pInfo->curr = marker; 1973 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Malformed tag on line %d"), lineNumber(pInfo)); 1974 return false; 1975 } 1976 switch (*marker) { 1977 case 'a': // Array 1978 if (markerLength == ARRAY_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[ARRAY_IX], ARRAY_TAG_LENGTH)) 1979 markerIx = ARRAY_IX; 1980 break; 1981 case 'd': // Dictionary, data, or date; Fortunately, they all have the same marker length.... 1982 if (markerLength != DICT_TAG_LENGTH) 1983 break; 1984 if (!memcmp(marker, CFXMLPlistTags[DICT_IX], DICT_TAG_LENGTH)) 1985 markerIx = DICT_IX; 1986 else if (!memcmp(marker, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH)) 1987 markerIx = DATA_IX; 1988 else if (!memcmp(marker, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH)) 1989 markerIx = DATE_IX; 1990 break; 1991 case 'f': // false (boolean) 1992 if (markerLength == FALSE_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH)) { 1993 markerIx = FALSE_IX; 1994 } 1995 break; 1996 case 'i': // integer 1997 if (markerLength == INTEGER_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH)) 1998 markerIx = INTEGER_IX; 1999 break; 2000 case 'k': // Key of a dictionary 2001 if (markerLength == KEY_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH)) { 2002 markerIx = KEY_IX; 2003 if (isKey) *isKey = true; 2004 } 2005 break; 2006 case 'p': // Plist 2007 if (markerLength == PLIST_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH)) 2008 markerIx = PLIST_IX; 2009 break; 2010 case 'r': // real 2011 if (markerLength == REAL_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH)) 2012 markerIx = REAL_IX; 2013 break; 2014 case 's': // String 2015 if (markerLength == STRING_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH)) 2016 markerIx = STRING_IX; 2017 break; 2018 case 't': // true (boolean) 2019 if (markerLength == TRUE_TAG_LENGTH && !memcmp(marker, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH)) 2020 markerIx = TRUE_IX; 2021 break; 2022 } 2023 2024 if (!pInfo->allowNewTypes && markerIx != PLIST_IX && markerIx != ARRAY_IX && markerIx != DICT_IX && markerIx != STRING_IX && markerIx != KEY_IX && markerIx != DATA_IX) { 2025 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered new tag when expecting only old-style property list objects")); 2026 return false; 2027 } 2028 2029 switch (markerIx) { 2030 case PLIST_IX: 2031 if (isEmpty) { 2032 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty plist tag")); 2033 return false; 2034 } 2035 return parsePListTag(pInfo, out); 2036 case ARRAY_IX: 2037 if (isEmpty) { 2038 if (pInfo->skip) { 2039 *out = NULL; 2040 } else { 2041 if (pInfo->mutabilityOption == kCFPropertyListImmutable) { 2042 *out = CFArrayCreate(pInfo->allocator, NULL, 0, &kCFTypeArrayCallBacks); 2043 } else { 2044 *out = CFArrayCreateMutable(pInfo->allocator, 0, &kCFTypeArrayCallBacks); 2045 } 2046 } 2047 return true; 2048 } else { 2049 return parseArrayTag(pInfo, out); 2050 } 2051 case DICT_IX: 2052 if (isEmpty) { 2053 if (pInfo->skip) { 2054 *out = NULL; 2055 } else { 2056 if (pInfo->mutabilityOption == kCFPropertyListImmutable) { 2057 *out = CFDictionaryCreate(pInfo->allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 2058 } else { 2059 *out = CFDictionaryCreateMutable(pInfo->allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 2060 } 2061 } 2062 return true; 2063 } else { 2064 return parseDictTag(pInfo, out); 2065 } 2066 case KEY_IX: 2067 case STRING_IX: 2068 { 2069 int tagLen = (markerIx == KEY_IX) ? KEY_TAG_LENGTH : STRING_TAG_LENGTH; 2070 if (isEmpty) { 2071 if (pInfo->skip) { 2072 *out = NULL; 2073 } else { 2074 if (pInfo->mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { 2075 *out = CFStringCreateMutable(pInfo->allocator, 0); 2076 } else { 2077 *out = CFStringCreateWithCharacters(pInfo->allocator, NULL, 0); 2078 } 2079 } 2080 return true; 2081 } 2082 if (!parseStringTag(pInfo, (CFStringRef *)out)) { 2083 return false; // parseStringTag will already have set the error string 2084 } 2085 if (!checkForCloseTag(pInfo, CFXMLPlistTags[markerIx], tagLen)) { 2086 __CFPListRelease(*out, pInfo->allocator); 2087 return false; 2088 } else { 2089 return true; 2090 } 2091 } 2092 case DATA_IX: 2093 if (isEmpty) { 2094 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <data> on line %d"), lineNumber(pInfo)); 2095 return false; 2096 } else { 2097 return parseDataTag(pInfo, out); 2098 } 2099 case DATE_IX: 2100 if (isEmpty) { 2101 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <date> on line %d"), lineNumber(pInfo)); 2102 return false; 2103 } else { 2104 return parseDateTag(pInfo, out); 2105 } 2106 case TRUE_IX: 2107 if (!isEmpty) { 2108 if (!checkForCloseTag(pInfo, CFXMLPlistTags[TRUE_IX], TRUE_TAG_LENGTH)) { 2109 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered non-empty <true> on line %d"), lineNumber(pInfo)); 2110 return false; 2111 } 2112 } 2113 if (pInfo->skip) { 2114 *out = NULL; 2115 } else { 2116 *out = CFRetain(kCFBooleanTrue); 2117 } 2118 return true; 2119 case FALSE_IX: 2120 if (!isEmpty) { 2121 if (!checkForCloseTag(pInfo, CFXMLPlistTags[FALSE_IX], FALSE_TAG_LENGTH)) { 2122 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered non-empty <false> on line %d"), lineNumber(pInfo)); 2123 return false; 2124 } 2125 } 2126 if (pInfo->skip) { 2127 *out = NULL; 2128 } else { 2129 *out = CFRetain(kCFBooleanFalse); 2130 } 2131 return true; 2132 case REAL_IX: 2133 if (isEmpty) { 2134 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <real> on line %d"), lineNumber(pInfo)); 2135 return false; 2136 } else { 2137 return parseRealTag(pInfo, out); 2138 } 2139 case INTEGER_IX: 2140 if (isEmpty) { 2141 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered empty <integer> on line %d"), lineNumber(pInfo)); 2142 return false; 2143 } else { 2144 return parseIntegerTag(pInfo, out); 2145 } 2146 default: { 2147 CFStringRef markerStr = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)marker, markerLength, kCFStringEncodingUTF8, NO); 2148 pInfo->curr = marker; 2149 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown tag %@ on line %d"), markerStr ? markerStr : CFSTR("<unknown>"), lineNumber(pInfo)); 2150 if (markerStr) CFRelease(markerStr); 2151 return false; 2152 } 2153 } 2154} 2155 2156static Boolean parseXMLPropertyList(_CFXMLPlistParseInfo *pInfo, CFTypeRef *out) { 2157 while (!pInfo->error && pInfo->curr < pInfo->end) { 2158 UniChar ch; 2159 skipWhitespace(pInfo); 2160 if (pInfo->curr+1 >= pInfo->end) { 2161 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("No XML content found")); 2162 return false; 2163 } 2164 if (*(pInfo->curr) != '<') { 2165 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Unexpected character %c at line %d"), *(pInfo->curr), lineNumber(pInfo)); 2166 return false; 2167 } 2168 ch = *(++ pInfo->curr); 2169 if (ch == '!') { 2170 // Comment or DTD 2171 ++ pInfo->curr; 2172 if (pInfo->curr+1 < pInfo->end && *pInfo->curr == '-' && *(pInfo->curr+1) == '-') { 2173 // Comment 2174 pInfo->curr += 2; 2175 skipXMLComment(pInfo); 2176 } else { 2177 skipDTD(pInfo); 2178 } 2179 } else if (ch == '?') { 2180 // Processing instruction 2181 pInfo->curr++; 2182 skipXMLProcessingInstruction(pInfo); 2183 } else { 2184 // Tag or malformed 2185 return parseXMLElement(pInfo, NULL, out); 2186 // Note we do not verify that there was only one element, so a file that has garbage after the first element will nonetheless successfully parse 2187 } 2188 } 2189 // Should never get here 2190 if (!(pInfo->error)) { 2191 pInfo->error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unexpected EOF")); 2192 } 2193 return false; 2194} 2195 2196static CFStringEncoding encodingForXMLData(CFDataRef data, CFErrorRef *error, CFIndex *skip) { 2197 const uint8_t *bytes = (uint8_t *)CFDataGetBytePtr(data); 2198 UInt32 length = CFDataGetLength(data); 2199 const uint8_t *idx, *end; 2200 char quote; 2201 2202 // Check for the byte order mark first. If we find it, set the skip value so the parser doesn't attempt to parse the BOM itself. 2203 if (length > 4) { 2204 if (*bytes == 0x00 && *(bytes+1) == 0x00 && *(bytes+2) == 0xFE && *(bytes+3) == 0xFF) { 2205 *skip = 4; 2206 return kCFStringEncodingUTF32BE; 2207 } else if (*bytes == 0xFF && *(bytes+1) == 0xFE && *(bytes+2) == 0x00 && *(bytes+3) == 0x00) { 2208 *skip = 4; 2209 return kCFStringEncodingUTF32LE; 2210 } 2211 } 2212 2213 if (length > 3) { 2214 if (*bytes == 0xEF && *(bytes+1) == 0xBB && *(bytes+2) == 0xBF) { 2215 *skip = 3; 2216 return kCFStringEncodingUTF8; 2217 } 2218 } 2219 2220 if (length > 2) { 2221 if (*bytes == 0xFF && *(bytes+1) == 0xFE) { 2222 *skip = 2; 2223 return kCFStringEncodingUTF16LE; 2224 } else if (*bytes == 0xFE && *(bytes+1) == 0xFF) { 2225 *skip = 2; 2226 return kCFStringEncodingUTF16BE; 2227 } else if (*bytes == 0x00 || *(bytes+1) == 0x00) { // This clause checks for a Unicode sequence lacking the byte order mark; technically an error, but this check is recommended by the XML spec 2228 *skip = 2; 2229 return kCFStringEncodingUnicode; 2230 } 2231 } 2232 2233 // Scan for the <?xml.... ?> opening 2234 if (length < 5 || strncmp((char const *) bytes, "<?xml", 5) != 0) return kCFStringEncodingUTF8; 2235 idx = bytes + 5; 2236 end = bytes + length; 2237 // Found "<?xml"; now we scan for "encoding" 2238 while (idx < end) { 2239 uint8_t ch = *idx; 2240 const uint8_t *scan; 2241 if ( ch == '?' || ch == '>') return kCFStringEncodingUTF8; 2242 idx ++; 2243 scan = idx; 2244 if (idx + 8 >= end) { 2245 if (error) *error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("End of buffer while looking for encoding name")); 2246 return 0; 2247 } 2248 if (ch == 'e' && *scan++ == 'n' && *scan++ == 'c' && *scan++ == 'o' && *scan++ == 'd' && *scan++ == 'i' 2249 && *scan++ == 'n' && *scan++ == 'g' && *scan++ == '=') { 2250 idx = scan; 2251 break; 2252 } 2253 } 2254 if (idx >= end) return kCFStringEncodingUTF8; 2255 quote = *idx; 2256 if (quote != '\'' && quote != '\"') return kCFStringEncodingUTF8; 2257 else { 2258 CFStringRef encodingName; 2259 const uint8_t *base = idx+1; // Move past the quote character 2260 UInt32 len; 2261 idx ++; 2262 while (idx < end && *idx != quote) idx ++; 2263 if (idx >= end) return kCFStringEncodingUTF8; 2264 len = idx - base; 2265 if (len == 5 && (*base == 'u' || *base == 'U') && (base[1] == 't' || base[1] == 'T') && (base[2] == 'f' || base[2] == 'F') && (base[3] == '-') && (base[4] == '8')) 2266 return kCFStringEncodingUTF8; 2267 encodingName = CFStringCreateWithBytes(kCFAllocatorSystemDefault, base, len, kCFStringEncodingISOLatin1, false); 2268#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX 2269 CFStringEncoding enc = CFStringConvertIANACharSetNameToEncoding(encodingName); 2270 if (enc != kCFStringEncodingInvalidId) { 2271 CFRelease(encodingName); 2272 return enc; 2273 } 2274#endif 2275 2276 if (error) { 2277 *error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown encoding (%@)"), encodingName); 2278 CFRelease(encodingName); 2279 } 2280 return 0; 2281 } 2282} 2283 2284bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString); 2285 2286#define SAVE_PLISTS 0 2287 2288#if SAVE_PLISTS 2289static Boolean __savePlistData(CFDataRef data, CFOptionFlags opt) { 2290 uint8_t pn[2048]; 2291 uint8_t fn[2048]; 2292 uint32_t pnlen = sizeof(pn); 2293 uint8_t *pnp = NULL; 2294 if (0 == _NSGetExecutablePath((char *)pn, &pnlen)) { 2295 pnp = strrchr((char *)pn, '/'); 2296 } 2297 if (!pnp) { 2298 pnp = pn; 2299 } else { 2300 pnp++; 2301 } 2302 if (0 == strcmp((char *)pnp, "parse_plists")) return true; 2303 CFUUIDRef r = CFUUIDCreate(kCFAllocatorSystemDefault); 2304 CFStringRef s = CFUUIDCreateString(kCFAllocatorSystemDefault, r); 2305 CFStringRef p = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("/tmp/plists/%s#%@#0x%x"), pnp, s, opt); 2306 _CFStringGetFileSystemRepresentation(p, fn, sizeof(fn)); 2307 CFRelease(r); 2308 CFRelease(s); 2309 CFRelease(p); 2310 int fd = open((const char *)fn, O_WRONLY|O_CREAT|O_TRUNC, 0666); 2311 if (fd < 0) return false; 2312 int len = CFDataGetLength(data); 2313 int w = write(fd, CFDataGetBytePtr(data), len); 2314 fsync(fd); 2315 close(fd); 2316 if (w != len) return false; 2317 return true; 2318} 2319#endif 2320 2321// If the data is from a converted string, then originalString is non-NULL. If originalString is NULL, then pass in guessedEncoding. 2322// keyPaths is a set of CFStrings, ':'-separated paths 2323static Boolean _CFPropertyListCreateFromUTF8Data(CFAllocatorRef allocator, CFDataRef xmlData, CFIndex skipBytes, CFStringRef originalString, CFStringEncoding guessedEncoding, CFOptionFlags option, CFErrorRef *outError, Boolean allowNewTypes, CFPropertyListFormat *format, CFSetRef keyPaths, CFTypeRef *out) { 2324 initStatics(); 2325 2326 CFAssert1(xmlData != NULL, __kCFLogAssertion, "%s(): NULL data not allowed", __PRETTY_FUNCTION__); 2327 CFAssert2(option == kCFPropertyListImmutable || option == kCFPropertyListMutableContainers || option == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, option); 2328 2329 CFIndex length = CFDataGetLength(xmlData); 2330 if (!length) { 2331 if (outError) *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Conversion of string failed. The string is empty.")); 2332 return false; 2333 } 2334 2335 _CFXMLPlistParseInfo pInfoBuf; 2336 _CFXMLPlistParseInfo *pInfo = &pInfoBuf; 2337 CFTypeRef result; 2338 2339 // Ensure that the data is not collected while we are using it 2340 CFRetain(xmlData); 2341 const char *buf = (const char *)CFDataGetBytePtr(xmlData); 2342 2343 // We may have to skip over starting stuff like BOM markers. 2344 buf += skipBytes; 2345 2346 pInfo->begin = buf; 2347 pInfo->end = buf+length; 2348 pInfo->curr = buf; 2349 pInfo->allocator = allocator; 2350 pInfo->error = NULL; 2351 _createStringMap(pInfo); 2352 pInfo->mutabilityOption = option; 2353 pInfo->allowNewTypes = allowNewTypes; 2354 pInfo->skip = false; 2355 pInfo->keyPaths = createTopLevelKeypaths(allocator, keyPaths); 2356 2357 Boolean success = parseXMLPropertyList(pInfo, &result); 2358 if (success && result && format) *format = kCFPropertyListXMLFormat_v1_0; 2359 2360 _cleanupStringMap(pInfo); 2361 if (pInfo->keyPaths && !(0)) CFRelease(pInfo->keyPaths); 2362 CFRelease(xmlData); 2363 2364 if (success) { 2365 *out = result; // caller releases 2366 return true; 2367 } 2368 2369 // Try again, old-style 2370 CFErrorRef oldStyleError = NULL; 2371 result = __CFCreateOldStylePropertyListOrStringsFile(allocator, xmlData, originalString, guessedEncoding, option, outError ? &oldStyleError : NULL, format); 2372 if (result) { 2373 // Release old error, return 2374 if (pInfo->error) CFRelease(pInfo->error); 2375 *out = result; 2376 return true; 2377 } 2378 2379 // Failure, both ways. Set up the error to be given back to caller. 2380 if (!outError) { 2381 if (pInfo->error) CFRelease(pInfo->error); 2382 return false; 2383 } 2384 2385 // Caller's responsibility to release outError 2386 if (pInfo->error && oldStyleError) { 2387 // Add the error from the old-style property list parser to the user info of the original error (pInfo->error), which had better exist 2388 CFDictionaryRef oldUserInfo = CFErrorCopyUserInfo(pInfo->error); 2389 CFMutableDictionaryRef newUserInfo = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, CFDictionaryGetCount(oldUserInfo) + 1, oldUserInfo); 2390 CFDictionaryAddValue(newUserInfo, CFPropertyListOldStyleParserErrorKey, oldStyleError); 2391 2392 // Re-create the xml parser error with this new user info dictionary 2393 CFErrorRef newError = CFErrorCreate(kCFAllocatorSystemDefault, CFErrorGetDomain(pInfo->error), CFErrorGetCode(pInfo->error), newUserInfo); 2394 2395 CFRelease(oldUserInfo); 2396 CFRelease(newUserInfo); 2397 CFRelease(oldStyleError); 2398 CFRelease(pInfo->error); 2399 *outError = newError; 2400 2401 } else if (pInfo->error && !oldStyleError) { 2402 // Return original error 2403 *outError = pInfo->error; 2404 } else if (!pInfo->error && oldStyleError) { 2405 // Return only old-style error 2406 // Probably shouldn't get here 2407 *outError = oldStyleError; 2408 } else if (!pInfo->error && !oldStyleError) { 2409 // Return unknown error 2410 *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Encountered unknown error during parse")); 2411 } 2412 return false; 2413} 2414 2415static CFDataRef _createUTF8DataFromString(CFAllocatorRef allocator, CFStringRef str) { 2416 CFIndex bytesNeeded = 0; 2417 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, false, NULL, 0, &bytesNeeded); 2418 2419 const char *bytes = (const char *)CFAllocatorAllocate(allocator, bytesNeeded, 0); 2420 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, 0, false, (uint8_t *)bytes, bytesNeeded, NULL); 2421 2422 CFDataRef utf8Data = CFDataCreateWithBytesNoCopy(allocator, (const UInt8 *)bytes, bytesNeeded, allocator); 2423 return utf8Data; 2424} 2425 2426// Set topLevelKeys to a set of top level keys to decode. If NULL, all keys are decoded. If the top level object is not a dictionary, all objects are decoded. If the plist is not XML, all objects are decoded. 2427static Boolean _CFPropertyListCreateWithData(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFErrorRef *outError, Boolean allowNewTypes, CFPropertyListFormat *format, CFSetRef topLevelKeys, CFTypeRef *out) { 2428 initStatics(); 2429 CFStringEncoding encoding; 2430 2431 if (!data || CFDataGetLength(data) == 0) { 2432 if (outError) { 2433 *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Cannot parse a NULL or zero-length data")); 2434 } 2435 return false; 2436 } 2437 2438#if SAVE_PLISTS 2439 __savePlistData(data, option); 2440#endif 2441 2442 // Ignore the error from CFTryParseBinaryPlist -- if it doesn't work, we're going to try again anyway using the XML parser 2443 if (__CFTryParseBinaryPlist(allocator, data, option, out, NULL)) { 2444 if (format) *format = kCFPropertyListBinaryFormat_v1_0; 2445 return true; 2446 } 2447 2448 // Use our own error variable here so we can check it against NULL later 2449 CFErrorRef subError = NULL; 2450 CFIndex skip = 0; 2451 encoding = encodingForXMLData(data, &subError, &skip); // 0 is an error return, NOT MacRoman. 2452 2453 if (encoding == 0) { 2454 // Couldn't find an encoding 2455 // Note that encodingForXMLData() will give us the right values for a standard plist, too. 2456 if (outError && subError == NULL) { 2457 // encodingForXMLData didn't set an error, so we create a new one here 2458 *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not determine the encoding of the XML data")); 2459 } else if (outError && subError) { 2460 // give the caller the subError, they will release 2461 *outError = subError; 2462 } else if (!outError && subError) { 2463 // Release the error 2464 CFRelease(subError); 2465 } 2466 return false; 2467 } 2468 2469 if (encoding == kCFStringEncodingUTF8) { 2470 // Use fast path 2471 return _CFPropertyListCreateFromUTF8Data(allocator, data, skip, NULL, encoding, option, outError, allowNewTypes, format, topLevelKeys, out); 2472 } 2473 2474 // Convert to UTF8 first 2475 CFStringRef xmlString = CFStringCreateWithBytes(allocator, CFDataGetBytePtr(data) + skip, CFDataGetLength(data) - skip, encoding, false); 2476 if (!xmlString) { 2477 if (outError) *outError = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("Could not determine the encoding of the XML data (string creation failed)")); 2478 return false; 2479 } 2480 2481 CFDataRef utf8Data = _createUTF8DataFromString(allocator, xmlString); 2482 2483 Boolean result = _CFPropertyListCreateFromUTF8Data(allocator, utf8Data, 0, xmlString, 0, option, outError, allowNewTypes, format, topLevelKeys, out); 2484 2485 if (xmlString && !(0)) CFRelease(xmlString); 2486 if (utf8Data && !(0)) CFRelease(utf8Data); 2487 2488 return result; 2489} 2490 2491// ----------------------------------------------------------------------------------------------------------------------- 2492 2493#pragma mark - 2494#pragma mark Exported Parsing Functions 2495 2496CFTypeRef _CFPropertyListCreateFromXMLStringError(CFAllocatorRef allocator, CFStringRef xmlString, CFOptionFlags option, CFErrorRef *error, Boolean allowNewTypes, CFPropertyListFormat *format) { 2497 // Convert to UTF8 first 2498 CFDataRef utf8Data = _createUTF8DataFromString(allocator, xmlString); 2499 CFTypeRef result = NULL; 2500 _CFPropertyListCreateFromUTF8Data(allocator, utf8Data, 0, xmlString, 0, option, error, allowNewTypes, format, NULL, &result); 2501 if (utf8Data && !(0)) CFRelease(utf8Data); 2502 2503 return result; 2504} 2505 2506CFTypeRef _CFPropertyListCreateFromXMLString(CFAllocatorRef allocator, CFStringRef xmlString, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format) { 2507 initStatics(); 2508 if (errorString) *errorString = NULL; 2509 CFErrorRef error = NULL; 2510 CFTypeRef result = _CFPropertyListCreateFromXMLStringError(allocator, xmlString, option, &error, allowNewTypes, format); 2511 2512 if (errorString && error) { 2513 // The caller is interested in receiving an error message. Pull the debug string out of the CFError returned above 2514 CFDictionaryRef userInfo = CFErrorCopyUserInfo(error); 2515 CFStringRef debugString = NULL; 2516 2517 // Handle a special-case for compatibility - if the XML parse failed and the old-style plist parse failed, construct a special string 2518 CFErrorRef underlyingError = NULL; 2519 2520 Boolean oldStyleFailed = CFDictionaryGetValueIfPresent(userInfo, CFPropertyListOldStyleParserErrorKey, (const void **)&underlyingError); 2521 2522 if (oldStyleFailed) { 2523 CFStringRef xmlParserErrorString, oldStyleParserErrorString; 2524 xmlParserErrorString = (CFStringRef)CFDictionaryGetValue(userInfo, kCFErrorDebugDescriptionKey); 2525 2526 CFDictionaryRef oldStyleParserUserInfo = CFErrorCopyUserInfo(underlyingError); 2527 oldStyleParserErrorString = (CFStringRef)CFDictionaryGetValue(userInfo, kCFErrorDebugDescriptionKey); 2528 2529 // Combine the two strings into a single one that matches the previous implementation 2530 debugString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("XML parser error:\n\t%@\nOld-style plist parser error:\n\t%@\n"), xmlParserErrorString, oldStyleParserErrorString); 2531 2532 CFRelease(oldStyleParserUserInfo); 2533 } else { 2534 debugString = (CFStringRef)CFDictionaryGetValue(userInfo, kCFErrorDebugDescriptionKey); 2535 if (debugString) CFRetain(debugString); 2536 } 2537 2538 // Give the debugString to the caller, who is responsible for releasing it 2539 CFRelease(userInfo); 2540 *errorString = debugString; 2541 } 2542 2543 if (error) { 2544 CFRelease(error); 2545 } 2546 2547 return result; 2548} 2549 2550CF_PRIVATE bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFSetRef keyPaths, CFPropertyListRef *plist); 2551 2552// Returns a subset of the property list, only including the key paths in the CFSet. 2553bool _CFPropertyListCreateFiltered(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFSetRef keyPaths, CFPropertyListRef *value, CFErrorRef *error) { 2554 2555 initStatics(); 2556 2557 if (!keyPaths || !data) { 2558 return false; 2559 } 2560 2561 uint8_t marker; 2562 CFBinaryPlistTrailer trailer; 2563 uint64_t offset; 2564 const uint8_t *databytes = CFDataGetBytePtr(data); 2565 uint64_t datalen = CFDataGetLength(data); 2566 Boolean success = false; 2567 CFTypeRef out = NULL; 2568 2569 // First check to see if it is a binary property list 2570 if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) { 2571 uint64_t valueOffset = offset; 2572 2573 // Split up the key path 2574 CFSetRef splitKeyPaths = createTopLevelKeypaths(allocator, keyPaths); 2575 2576 // Create a dictionary to cache objects in 2577 CFMutableDictionaryRef objects = CFDictionaryCreateMutable(allocator, 0, NULL, &kCFTypeDictionaryValueCallBacks); 2578 success = __CFBinaryPlistCreateObjectFiltered(databytes, datalen, valueOffset, &trailer, allocator, option, objects, NULL, 0, splitKeyPaths, &out); 2579 2580 CFRelease(splitKeyPaths); 2581 CFRelease(objects); 2582 } else { 2583 // Try an XML property list 2584 success = _CFPropertyListCreateWithData(allocator, data, option, error, true, NULL, keyPaths, &out); 2585 } 2586 2587 if (success && value) { 2588 *value = out; // caller releases 2589 } else if (out) { 2590 CFRelease(out); 2591 } 2592 return success; 2593} 2594 2595/* Get a single value for a given key in a top-level dictionary in a property list. 2596 @param allocator The allocator to use. 2597 @param data The property list data. 2598 @param option Currently unused, set to 0. 2599 @param keyPath The keyPath to search for in the property list. Keys are colon-separated. Indexes into arrays are specified with an integer (zero-based). 2600 @param value If the key is found and the parameter is non-NULL, this will be set to a reference to the value. It is the caller's responsibility to release the object. If no object is found, will be set to NULL. If this parameter is NULL, this function can be used to check for the existence of a key without creating it by just checking the return value. 2601 @param error If an error occurs, will be set to a valid CFErrorRef. It is the caller's responsibility to release this value. 2602 @return True if the key is found, false otherwise. 2603 */ 2604bool _CFPropertyListCreateSingleValue(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFStringRef keyPath, CFPropertyListRef *value, CFErrorRef *error) { 2605 2606 initStatics(); 2607 2608 if (!keyPath || CFStringGetLength(keyPath) == 0) { 2609 return false; 2610 } 2611 2612 uint8_t marker; 2613 CFBinaryPlistTrailer trailer; 2614 uint64_t offset; 2615 const uint8_t *databytes = CFDataGetBytePtr(data); 2616 uint64_t datalen = CFDataGetLength(data); 2617 Boolean success = false; 2618 2619 // First check to see if it is a binary property list 2620 if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) { 2621 // Split up the key path 2622 CFArrayRef keyPathArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefault, keyPath, CFSTR(":")); 2623 uint64_t keyOffset, valueOffset = offset; 2624 2625 // Create a dictionary to cache objects in 2626 CFMutableDictionaryRef objects = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); 2627 CFIndex keyPathCount = CFArrayGetCount(keyPathArray); 2628 _CFDictionarySetCapacity(objects, keyPathCount + 1); 2629 2630 for (CFIndex i = 0; i < keyPathCount; i++) { 2631 CFStringRef oneKey = (CFStringRef)CFArrayGetValueAtIndex(keyPathArray, i); 2632 SInt32 intValue = CFStringGetIntValue(oneKey); 2633 if ((intValue == 0 && CFStringCompare(CFSTR("0"), oneKey, 0) != kCFCompareEqualTo) || intValue == INT_MAX || intValue == INT_MIN || intValue < 0) { 2634 // Treat as a string key into a dictionary 2635 success = __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes, datalen, valueOffset, &trailer, (CFTypeRef)oneKey, &keyOffset, &valueOffset, false, objects); 2636 } else { 2637 // Treat as integer index into an array 2638 success = __CFBinaryPlistGetOffsetForValueFromArray2(databytes, datalen, valueOffset, &trailer, intValue, &valueOffset, objects); 2639 } 2640 2641 if (!success) { 2642 break; 2643 } 2644 } 2645 2646 // value could be null if the caller wanted to check for the existence of a key but not bother creating it 2647 if (success && value) { 2648 CFPropertyListRef pl; 2649 success = __CFBinaryPlistCreateObjectFiltered(databytes, datalen, valueOffset, &trailer, allocator, option, objects, NULL, 0, NULL, &pl); 2650 if (success) { 2651 // caller's responsibility to release the created object 2652 *value = pl; 2653 } 2654 } 2655 2656 CFRelease(keyPathArray); 2657 CFRelease(objects); 2658 } else { 2659 // Try an XML property list 2660 // Note: This is currently not any more efficient than grabbing the whole thing. This could be improved in the future. 2661 CFPropertyListRef plist = CFPropertyListCreateWithData(allocator, data, option, NULL, error); 2662 if (plist) { 2663 CFPropertyListRef nextObject = plist; 2664 success = true; 2665 CFArrayRef keyPathArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorSystemDefault, keyPath, CFSTR(":")); 2666 for (CFIndex i = 0; i < CFArrayGetCount(keyPathArray); i++) { 2667 CFStringRef oneKey = (CFStringRef)CFArrayGetValueAtIndex(keyPathArray, i); 2668 SInt32 intValue = CFStringGetIntValue(oneKey); 2669 if (((intValue == 0 && CFStringCompare(CFSTR("0"), oneKey, 0) != kCFCompareEqualTo) || intValue == INT_MAX || intValue == INT_MIN) && CFGetTypeID((CFTypeRef)nextObject) == dicttype) { 2670 // Treat as a string key into a dictionary 2671 nextObject = (CFPropertyListRef)CFDictionaryGetValue((CFDictionaryRef)nextObject, oneKey); 2672 } else if (CFGetTypeID((CFTypeRef)nextObject) == arraytype) { 2673 // Treat as integer index into an array 2674 nextObject = (CFPropertyListRef)CFArrayGetValueAtIndex((CFArrayRef)nextObject, intValue); 2675 } else { 2676 success = false; 2677 break; 2678 } 2679 } 2680 2681 if (success && nextObject && value) { 2682 *value = nextObject; 2683 // caller's responsibility to release the created object 2684 CFRetain(*value); 2685 } else if (!nextObject) { 2686 success = false; 2687 } 2688 2689 CFRelease(keyPathArray); 2690 CFRelease(plist); 2691 } 2692 } 2693 2694 return success; 2695} 2696 2697// Legacy 2698CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format) { 2699 initStatics(); 2700 CFTypeRef out = NULL; 2701 if (errorString) *errorString = NULL; 2702 CFErrorRef error = NULL; 2703 Boolean result = _CFPropertyListCreateWithData(allocator, xmlData, option, &error, allowNewTypes, format, NULL, &out); 2704 if (!result && error && errorString) { 2705 *errorString = __copyErrorDebugDescription(error); 2706 } 2707 if (error) CFRelease(error); 2708 return out; 2709} 2710 2711CFPropertyListRef CFPropertyListCreateWithData(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags options, CFPropertyListFormat *format, CFErrorRef *error) { 2712 initStatics(); 2713 CFAssert1(data != NULL, __kCFLogAssertion, "%s(): NULL data not allowed", __PRETTY_FUNCTION__); 2714 CFAssert2(options == kCFPropertyListImmutable || options == kCFPropertyListMutableContainers || options == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, options); 2715 CFPropertyListRef out = NULL; 2716 _CFPropertyListCreateWithData(allocator, data, options, error, true, format, NULL, &out); 2717 return out; 2718} 2719 2720CFPropertyListRef CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString) { 2721 initStatics(); 2722 if (errorString) *errorString = NULL; 2723 CFErrorRef error = NULL; 2724 CFPropertyListRef result = CFPropertyListCreateWithData(allocator, xmlData, option, NULL, &error); 2725 if (error && errorString) { 2726 *errorString = __copyErrorDebugDescription(error); 2727 } 2728 if (error) CFRelease(error); 2729 return result; 2730} 2731 2732CFDataRef CFPropertyListCreateData(CFAllocatorRef allocator, CFPropertyListRef propertyList, CFPropertyListFormat format, CFOptionFlags options, CFErrorRef *error) { 2733 initStatics(); 2734 CFAssert1(format != kCFPropertyListOpenStepFormat, __kCFLogAssertion, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__); 2735 CFAssert2(format == kCFPropertyListXMLFormat_v1_0 || format == kCFPropertyListBinaryFormat_v1_0, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, format); 2736 CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); 2737 __CFAssertIsPList(propertyList); 2738 2739 CFDataRef data = NULL; 2740 2741 2742 CFStringRef validErr = NULL; 2743 if (!_CFPropertyListIsValidWithErrorString(propertyList, format, &validErr)) { 2744 CFLog(kCFLogLevelError, CFSTR("Property list invalid for format: %d (%@)"), format, validErr); 2745 if (validErr) CFRelease(validErr); 2746 return NULL; 2747 } 2748 2749 if (format == kCFPropertyListOpenStepFormat) { 2750 CFLog(kCFLogLevelError, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing")); 2751 return NULL; 2752 } else if (format == kCFPropertyListXMLFormat_v1_0) { 2753 data = _CFPropertyListCreateXMLData(allocator, propertyList, true); 2754 } else if (format == kCFPropertyListBinaryFormat_v1_0) { 2755#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 2756 // TODO: Is it more efficient to create a stream here or just use a mutable data? 2757 CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorSystemDefault, allocator); 2758 CFWriteStreamOpen(stream); 2759 CFIndex len = CFPropertyListWrite(propertyList, stream, format, options, error); 2760 if (0 < len) { 2761 data = (CFDataRef)CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten); 2762 } 2763 CFWriteStreamClose(stream); 2764 CFRelease(stream); 2765#else 2766 CFMutableDataRef dataForPlist = CFDataCreateMutable(allocator, 0); 2767 __CFBinaryPlistWrite(propertyList, dataForPlist, 0, options, error); 2768 return dataForPlist; 2769#endif 2770 } else { 2771 CFLog(kCFLogLevelError, CFSTR("Unknown format option")); 2772 } 2773 2774 return data; 2775} 2776 2777#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 2778 2779CFIndex CFPropertyListWrite(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFOptionFlags options, CFErrorRef *error) { 2780 initStatics(); 2781 CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__); 2782 CFAssert1(format != kCFPropertyListOpenStepFormat, __kCFLogAssertion, "%s(): kCFPropertyListOpenStepFormat not supported for writing", __PRETTY_FUNCTION__); 2783 CFAssert2(format == kCFPropertyListXMLFormat_v1_0 || format == kCFPropertyListBinaryFormat_v1_0, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, format); 2784 CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); 2785 __CFAssertIsPList(propertyList); 2786 CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__); 2787 CFAssert1(kCFStreamStatusOpen == CFWriteStreamGetStatus(stream) || kCFStreamStatusWriting == CFWriteStreamGetStatus(stream), __kCFLogAssertion, "%s(): stream is not open", __PRETTY_FUNCTION__); 2788 2789 CFStringRef validErr = NULL; 2790 if (!_CFPropertyListIsValidWithErrorString(propertyList, format, &validErr)) { 2791 CFLog(kCFLogLevelError, CFSTR("Property list invalid for format: %d (%@)"), format, validErr); 2792 if (validErr) CFRelease(validErr); 2793 return 0; 2794 } 2795 if (format == kCFPropertyListOpenStepFormat) { 2796 CFLog(kCFLogLevelError, CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing")); 2797 return 0; 2798 } 2799 if (format == kCFPropertyListXMLFormat_v1_0) { 2800 CFDataRef data = _CFPropertyListCreateXMLData(kCFAllocatorSystemDefault, propertyList, true); 2801 if (!data) { 2802 CFLog(kCFLogLevelError, CFSTR("Property list format kCFPropertyListXMLFormat_v1_0 specified but was not a valid property list type")); 2803 return 0; 2804 } 2805 CFIndex len = CFDataGetLength(data); 2806 const uint8_t *ptr = CFDataGetBytePtr(data); 2807 while (0 < len) { 2808 CFIndex ret = CFWriteStreamWrite(stream, ptr, len); 2809 if (ret == 0) { 2810 if (error) *error = __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Property list writing could not be completed because stream is full.")); 2811 CFRelease(data); 2812 return 0; 2813 } 2814 if (ret < 0) { 2815 CFErrorRef underlyingError = CFWriteStreamCopyError(stream); 2816 if (underlyingError) { 2817 if (error) { 2818 // Wrap the error from CFWriteStreamCopy in a new error 2819 CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 2820 CFDictionarySetValue(userInfo, kCFErrorDebugDescriptionKey, CFSTR("Property list writing could not be completed because the stream had an unknown error.")); 2821 CFDictionarySetValue(userInfo, kCFErrorUnderlyingErrorKey, underlyingError); 2822 *error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainCocoa, kCFPropertyListWriteStreamError, userInfo); 2823 CFRelease(userInfo); 2824 } 2825 CFRelease(underlyingError); 2826 } 2827 CFRelease(data); 2828 return 0; 2829 } 2830 ptr += ret; 2831 len -= ret; 2832 } 2833 len = CFDataGetLength(data); 2834 CFRelease(data); 2835 return len; 2836 } 2837 if (format == kCFPropertyListBinaryFormat_v1_0) { 2838 CFIndex len = __CFBinaryPlistWrite(propertyList, stream, 0, options, error); 2839 return len; 2840 } 2841 CFLog(kCFLogLevelError, CFSTR("Unknown format option")); 2842 return 0; 2843} 2844 2845CFIndex CFPropertyListWriteToStream(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFStringRef *errorString) { 2846 initStatics(); 2847 if (errorString) *errorString = NULL; 2848 CFErrorRef error = NULL; 2849 2850 // For backwards compatibility, we check the format parameter up front since these do not have CFError counterparts in the newer API 2851 CFStringRef validErr = NULL; 2852 if (!_CFPropertyListIsValidWithErrorString(propertyList, format, &validErr)) { 2853 if (errorString) *errorString = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("Property list invalid for format (%@)"), validErr); 2854 if (validErr) CFRelease(validErr); 2855 return 0; 2856 } 2857 if (format == kCFPropertyListOpenStepFormat) { 2858 if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing")); 2859 return 0; 2860 } 2861 if (format != kCFPropertyListBinaryFormat_v1_0 && format != kCFPropertyListXMLFormat_v1_0) { 2862 if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("Unknown format option")); 2863 return 0; 2864 } 2865 2866 CFIndex result = CFPropertyListWrite(propertyList, stream, format, 0, &error); 2867 if (error && errorString) { 2868 *errorString = __copyErrorDebugDescription(error); 2869 } 2870 if (error) CFRelease(error); 2871 return result; 2872} 2873 2874static bool __convertReadStreamToBytes(CFReadStreamRef stream, CFIndex max, uint8_t **buffer, CFIndex *length, CFErrorRef *error) { 2875 int32_t buflen = 0, bufsize = 0, retlen; 2876 uint8_t *buf = NULL, sbuf[8192]; 2877 for (;;) { 2878 retlen = CFReadStreamRead(stream, sbuf, __CFMin(8192, max)); 2879 if (retlen <= 0) { 2880 *buffer = buf; 2881 *length = buflen; 2882 2883 if (retlen < 0 && error) { 2884 // Copy the error out 2885 *error = CFReadStreamCopyError(stream); 2886 return false; 2887 } 2888 2889 return true; 2890 } 2891 if (bufsize < buflen + retlen) { 2892 if (bufsize < 256 * 1024) { 2893 bufsize *= 4; 2894 } else if (bufsize < 16 * 1024 * 1024) { 2895 bufsize *= 2; 2896 } else { 2897 // once in this stage, this will be really slow 2898 // and really potentially fragment memory 2899 bufsize += 256 * 1024; 2900 } 2901 if (bufsize < buflen + retlen) bufsize = buflen + retlen; 2902 buf = (uint8_t *)CFAllocatorReallocate(kCFAllocatorSystemDefault, buf, bufsize, 0); 2903 if (!buf) HALT; 2904 } 2905 memmove(buf + buflen, sbuf, retlen); 2906 buflen += retlen; 2907 max -= retlen; 2908 if (max <= 0) { 2909 *buffer = buf; 2910 *length = buflen; 2911 return true; 2912 } 2913 } 2914 return true; 2915} 2916 2917CFPropertyListRef CFPropertyListCreateWithStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex streamLength, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFErrorRef *error) { 2918 initStatics(); 2919 2920 CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__); 2921 CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__); 2922 CFAssert1(kCFStreamStatusOpen == CFReadStreamGetStatus(stream) || kCFStreamStatusReading == CFReadStreamGetStatus(stream), __kCFLogAssertion, "%s(): stream is not open", __PRETTY_FUNCTION__); 2923 CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption); 2924 2925 if (0 == streamLength) streamLength = LONG_MAX; 2926 CFErrorRef underlyingError = NULL; 2927 CFIndex buflen = 0; 2928 uint8_t *buffer = NULL; 2929 if (!__convertReadStreamToBytes(stream, streamLength, &buffer, &buflen, &underlyingError)) { 2930 if (error) { 2931 // Wrap the error from CFReadStream in a new error in the cocoa domain 2932 CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 2933 CFDictionarySetValue(userInfo, kCFErrorDebugDescriptionKey, CFSTR("Property list reading could not be completed because the stream had an unknown error. Did you forget to open the stream?")); 2934 if (underlyingError) { 2935 CFDictionarySetValue(userInfo, kCFErrorUnderlyingErrorKey, underlyingError); 2936 } 2937 *error = CFErrorCreate(kCFAllocatorSystemDefault, kCFErrorDomainCocoa, kCFPropertyListReadStreamError, userInfo); 2938 CFRelease(userInfo); 2939 } 2940 if (underlyingError) { 2941 CFRelease(underlyingError); 2942 } 2943 return NULL; 2944 } 2945 2946 if (!buffer || buflen < 6) { 2947 if (buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, buffer); 2948 if (error) *error = __CFPropertyListCreateError(kCFPropertyListReadCorruptError, CFSTR("stream had too few bytes")); 2949 return NULL; 2950 } 2951 2952 CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, buffer, buflen, kCFAllocatorSystemDefault); 2953 CFPropertyListRef pl = NULL; // initialize to null, because if the following call fails we must return NULL 2954 _CFPropertyListCreateWithData(allocator, data, mutabilityOption, error, true, format, NULL, &pl); 2955 CFRelease(data); 2956 2957 return pl; 2958} 2959 2960CFPropertyListRef CFPropertyListCreateFromStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex length, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFStringRef *errorString) { 2961 initStatics(); 2962 if (errorString) *errorString = NULL; 2963 CFErrorRef error = NULL; 2964 CFPropertyListRef result = CFPropertyListCreateWithStream(allocator, stream, length, mutabilityOption, format, &error); 2965 if (error && errorString) { 2966 *errorString = __copyErrorDebugDescription(error); 2967 } 2968 if (error) CFRelease(error); 2969 return result; 2970} 2971 2972#endif //DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS 2973 2974#pragma mark - 2975#pragma mark Property List Copies 2976 2977static CFArrayRef _arrayDeepImmutableCopy(CFAllocatorRef allocator, CFArrayRef array, CFOptionFlags mutabilityOption) { 2978 CFArrayRef result = NULL; 2979 CFIndex i, c = CFArrayGetCount(array); 2980 if (c == 0) { 2981 result = CFArrayCreate(allocator, NULL, 0, &kCFTypeArrayCallBacks); 2982 } else { 2983 new_cftype_array(values, c); 2984 CFArrayGetValues(array, CFRangeMake(0, c), values); 2985 for (i = 0; i < c; i ++) { 2986 CFTypeRef newValue = CFPropertyListCreateDeepCopy(allocator, values[i], mutabilityOption); 2987 if (newValue == NULL) { 2988 break; 2989 } 2990 __CFAssignWithWriteBarrier((void **)values + i, (void *)newValue); 2991 } 2992 result = (i == c) ? CFArrayCreate(allocator, values, c, &kCFTypeArrayCallBacks) : NULL; 2993 c = i; 2994 for (i = 0; i < c; i ++) CFRelease(values[i]); 2995 free_cftype_array(values); 2996 } 2997 return result; 2998} 2999 3000static CFMutableArrayRef _arrayDeepMutableCopy(CFAllocatorRef allocator, CFArrayRef array, CFOptionFlags mutabilityOption) { 3001 CFIndex i, c = CFArrayGetCount(array); 3002 CFMutableArrayRef result = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks); 3003 if (result) { 3004 for (i = 0; i < c; i ++) { 3005 CFTypeRef newValue = CFPropertyListCreateDeepCopy(allocator, CFArrayGetValueAtIndex(array, i), mutabilityOption); 3006 if (!newValue) break; 3007 CFArrayAppendValue(result, newValue); 3008 CFRelease(newValue); 3009 } 3010 if (i != c) { 3011 CFRelease(result); 3012 result = NULL; 3013 } 3014 } 3015 return result; 3016} 3017 3018CFPropertyListRef CFPropertyListCreateDeepCopy(CFAllocatorRef allocator, CFPropertyListRef propertyList, CFOptionFlags mutabilityOption) { 3019 initStatics(); 3020 CFPropertyListRef result = NULL; 3021 CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): cannot copy a NULL property list", __PRETTY_FUNCTION__); 3022 __CFAssertIsPList(propertyList); 3023 CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption); 3024 if (!CFPropertyListIsValid(propertyList, kCFPropertyListBinaryFormat_v1_0)) return NULL; 3025 3026 CFTypeID typeID = CFGetTypeID(propertyList); 3027 if (typeID == dicttype) { 3028 CFDictionaryRef dict = (CFDictionaryRef)propertyList; 3029 Boolean isMutable = (mutabilityOption != kCFPropertyListImmutable); 3030 CFIndex count = CFDictionaryGetCount(dict); 3031 if (count == 0) { 3032 result = isMutable ? CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks): CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 3033 } else { 3034 new_cftype_array(keys, 2 * count); 3035 CFTypeRef *values; 3036 CFIndex i; 3037 values = keys+count; 3038 CFDictionaryGetKeysAndValues(dict, keys, values); 3039 for (i = 0; i < count; i ++) { 3040 CFTypeRef newKey = CFStringCreateCopy(allocator, (CFStringRef)keys[i]); 3041 if (newKey == NULL) { 3042 break; 3043 } 3044 __CFAssignWithWriteBarrier((void **)keys + i, (void *)newKey); 3045 CFTypeRef newValue = CFPropertyListCreateDeepCopy(allocator, values[i], mutabilityOption); 3046 if (newValue == NULL) { 3047 CFRelease(keys[i]); 3048 break; 3049 } 3050 __CFAssignWithWriteBarrier((void **)values + i, (void *)newValue); 3051 } 3052 if (i == count) { 3053 result = isMutable ? CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) : CFDictionaryCreate(allocator, keys, values, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 3054 for (i = 0; i < count; i ++) { 3055 if (isMutable) { 3056 CFDictionarySetValue((CFMutableDictionaryRef)result, keys[i], values[i]); 3057 } 3058 CFRelease(keys[i]); 3059 CFRelease(values[i]); 3060 } 3061 } else { 3062 result = NULL; 3063 count = i; 3064 for (i = 0; i < count; i ++) { 3065 CFRelease(keys[i]); 3066 CFRelease(values[i]); 3067 } 3068 } 3069 free_cftype_array(keys); 3070 } 3071 } else if (typeID == arraytype) { 3072 if (mutabilityOption == kCFPropertyListImmutable) { 3073 result = _arrayDeepImmutableCopy(allocator, (CFArrayRef)propertyList, mutabilityOption); 3074 } else { 3075 result = _arrayDeepMutableCopy(allocator, (CFArrayRef)propertyList, mutabilityOption); 3076 } 3077 } else if (typeID == datatype) { 3078 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { 3079 result = CFDataCreateMutableCopy(allocator, 0, (CFDataRef)propertyList); 3080 } else { 3081 result = CFDataCreateCopy(allocator, (CFDataRef)propertyList); 3082 } 3083 } else if (typeID == numbertype) { 3084 // Warning - this will break if byteSize is ever greater than 128 3085 uint8_t bytes[128]; 3086 CFNumberType numType = _CFNumberGetType2((CFNumberRef)propertyList); 3087 CFNumberGetValue((CFNumberRef)propertyList, numType, (void *)bytes); 3088 result = CFNumberCreate(allocator, numType, (void *)bytes); 3089 } else if (typeID == booltype) { 3090 // Booleans are immutable & shared instances 3091 CFRetain(propertyList); 3092 result = propertyList; 3093 } else if (typeID == datetype) { 3094 // Dates are immutable 3095 result = CFDateCreate(allocator, CFDateGetAbsoluteTime((CFDateRef)propertyList)); 3096 } else if (typeID == stringtype) { 3097 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { 3098 result = CFStringCreateMutableCopy(allocator, 0, (CFStringRef)propertyList); 3099 } else { 3100 result = CFStringCreateCopy(allocator, (CFStringRef)propertyList); 3101 } 3102 } else { 3103 CFAssert2(false, __kCFLogAssertion, "%s(): %p is not a property list type", __PRETTY_FUNCTION__, propertyList); 3104 result = NULL; 3105 } 3106 return result; 3107} 3108 3109