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