]> git.saurik.com Git - apple/cf.git/blob - CFPropertyList.c
CF-1152.14.tar.gz
[apple/cf.git] / 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, "&lt;");
421 break;
422 case '>':
423 if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
424 bufCnt = 0;
425 _plistAppendUTF8CString(mStr, "&gt;");
426 break;
427 case '&':
428 if (bufCnt) _plistAppendCharacters(mStr, buf, bufCnt);
429 bufCnt = 0;
430 _plistAppendUTF8CString(mStr, "&amp;");
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