1/*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*	CFXMLNode.c
25	Copyright (c) 1998-2013, Apple Inc. All rights reserved.
26	Responsibility: David Smith
27*/
28
29#include <CoreFoundation/CFXMLNode.h>
30#include <CoreFoundation/CFPropertyList.h>
31#include "CFInternal.h"
32#include "CFXMLInputStream.h"
33
34CF_INLINE Boolean _nullSafeCFEqual(CFTypeRef cf1, CFTypeRef cf2) {
35    if (cf1 && !cf2) return false;
36    if (cf2 && !cf1) return false;
37    if (cf1) return CFEqual(cf1, cf2);
38    return true;
39}
40
41static Boolean externalIDEqual(CFXMLExternalID *ext1, CFXMLExternalID *ext2) {
42    return _nullSafeCFEqual(ext1->systemID, ext2->systemID) && _nullSafeCFEqual(ext1->publicID, ext2->publicID);
43}
44
45static Boolean __CFXMLNodeEqual(CFTypeRef  cf1, CFTypeRef  cf2) {
46    CFXMLNodeRef desc1 = (CFXMLNodeRef)cf1, desc2 = (CFXMLNodeRef)cf2;
47    if (desc1 == desc2) return true;
48    if (!desc1 || !desc2) return false;
49    if (desc1->dataTypeID != desc2->dataTypeID) return false;
50    if ((desc1->dataString && !desc2->dataString) || (!desc1->dataString && desc2->dataString)) return false;
51    if (desc1->dataString && !CFEqual(desc1->dataString, desc2->dataString)) return false;
52    if ((desc1->additionalData && !desc2->additionalData) || (!desc1->additionalData && desc2->additionalData)) return false;
53    if (!desc1->additionalData) return true;
54    switch (desc1->dataTypeID) {
55        case kCFXMLNodeTypeDocument:{
56            CFURLRef url1, url2;
57            url1 = ((CFXMLDocumentInfo *)desc1->additionalData)->sourceURL;
58            url2 = ((CFXMLDocumentInfo *)desc2->additionalData)->sourceURL;
59            return _nullSafeCFEqual(url1, url2);
60        }
61        case kCFXMLNodeTypeElement: {
62            CFXMLElementInfo *elt1, *elt2;
63            elt1 = (CFXMLElementInfo *)desc1->additionalData;
64            elt2 = (CFXMLElementInfo *)desc2->additionalData;
65            if (elt1->isEmpty != elt2->isEmpty) return false;
66            if (elt1->attributes == elt2->attributes) return true;
67            if (!elt1->attributes) return (CFDictionaryGetCount(elt2->attributes) == 0);
68            if (!elt2->attributes) return (CFDictionaryGetCount(elt1->attributes) == 0);
69            return CFEqual(elt1->attributes, elt2->attributes);
70        }
71        case kCFXMLNodeTypeProcessingInstruction: {
72            CFStringRef str1, str2;
73            str1 = ((CFXMLProcessingInstructionInfo *)desc1->additionalData)->dataString;
74            str2 = ((CFXMLProcessingInstructionInfo *)desc2->additionalData)->dataString;
75            return _nullSafeCFEqual(str1, str2);
76        }
77        case kCFXMLNodeTypeEntity: {
78            CFXMLEntityInfo *data1, *data2;
79            data1 = (CFXMLEntityInfo *)desc1->additionalData;
80            data2 = (CFXMLEntityInfo *)desc2->additionalData;
81            if (data1->entityType != data2->entityType) return false;
82            if (!_nullSafeCFEqual(data1->replacementText, data2->replacementText)) return false;
83            if (!_nullSafeCFEqual(data1->notationName, data2->notationName)) return false;
84            return externalIDEqual(&data1->entityID, &data2->entityID);
85        }
86        case kCFXMLNodeTypeEntityReference: {
87            return ((CFXMLEntityReferenceInfo *)(desc1->additionalData))->entityType == ((CFXMLEntityReferenceInfo *)(desc2->additionalData))->entityType;
88        }
89        case kCFXMLNodeTypeNotation: {
90            CFXMLNotationInfo *data1, *data2;
91            data1 = (CFXMLNotationInfo *)desc1->additionalData;
92            data2 = (CFXMLNotationInfo *)desc2->additionalData;
93            return externalIDEqual(&(data1->externalID), &(data2->externalID));
94        }
95        case kCFXMLNodeTypeDocumentType: {
96            CFXMLDocumentTypeInfo *data1, *data2;
97            data1 = (CFXMLDocumentTypeInfo *)desc1->additionalData;
98            data2 = (CFXMLDocumentTypeInfo *)desc2->additionalData;
99            return externalIDEqual(&(data1->externalID), &(data2->externalID));
100        }
101        case kCFXMLNodeTypeElementTypeDeclaration: {
102            CFXMLElementTypeDeclarationInfo *d1 = (CFXMLElementTypeDeclarationInfo *)desc1->additionalData;
103            CFXMLElementTypeDeclarationInfo *d2 = (CFXMLElementTypeDeclarationInfo *)desc2->additionalData;
104            return _nullSafeCFEqual(d1->contentDescription, d2->contentDescription);
105        }
106        case kCFXMLNodeTypeAttributeListDeclaration: {
107            CFXMLAttributeListDeclarationInfo *attList1 = (CFXMLAttributeListDeclarationInfo *)desc1->additionalData;
108            CFXMLAttributeListDeclarationInfo *attList2 = (CFXMLAttributeListDeclarationInfo *)desc2->additionalData;
109            CFIndex idx;
110            if (attList1->numberOfAttributes != attList2->numberOfAttributes) return false;
111            for (idx = 0; idx < attList1->numberOfAttributes; idx ++) {
112                CFXMLAttributeDeclarationInfo *attr1 = &(attList1->attributes[idx]);
113                CFXMLAttributeDeclarationInfo *attr2 = &(attList2->attributes[idx]);
114                if (!_nullSafeCFEqual(attr1->attributeName, attr2->attributeName)) return false;
115                if (!_nullSafeCFEqual(attr1->typeString, attr2->typeString)) return false;
116                if (!_nullSafeCFEqual(attr1->defaultString, attr2->defaultString)) return false;
117            }
118            return true;
119        }
120        default:
121            return false;
122    }
123    return true;
124}
125
126static CFHashCode __CFXMLNodeHash(CFTypeRef  cf) {
127    CFXMLNodeRef node = (CFXMLNodeRef)cf;
128    if (node->dataString) {
129        return CFHash(node->dataString);
130    }
131    if (node->dataTypeID == kCFXMLNodeTypeDocument) {
132        CFURLRef url = ((CFXMLDocumentInfo *)node->additionalData)->sourceURL;
133        return url ? CFHash(url) : (CFHashCode)cf;
134    } else {
135        CFAssert2(false, __kCFLogAssertion, "%s(): Saw unexpected XML type code %d", __PRETTY_FUNCTION__, node->dataTypeID);
136        return CFHash(cf);
137    }
138}
139
140static CFStringRef __CFXMLNodeCopyDescription(CFTypeRef  cf) {
141    struct __CFXMLNode *node = (struct __CFXMLNode *)cf;
142    return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFXMLNode %p>{typeID = %ld, string = %@}"), cf, (unsigned long)node->dataTypeID, node->dataString);
143}
144
145static void __CFXMLNodeDeallocate(CFTypeRef  cf) {
146    struct __CFXMLNode *node = (struct __CFXMLNode *)cf;
147    if (node->dataString) CFRelease(node->dataString);
148    if (node->additionalData) {
149        switch (node->dataTypeID) {
150            case kCFXMLNodeTypeDocument:
151                if (((CFXMLDocumentInfo *)node->additionalData)->sourceURL) {
152                    CFRelease(((CFXMLDocumentInfo *)node->additionalData)->sourceURL);
153                }
154                break;
155            case kCFXMLNodeTypeElement:
156                if (((CFXMLElementInfo *)node->additionalData)->attributes) {
157                    CFRelease(((CFXMLElementInfo *)node->additionalData)->attributes);
158                    CFRelease(((CFXMLElementInfo *)node->additionalData)->attributeOrder);
159                }
160                break;
161            case kCFXMLNodeTypeProcessingInstruction:
162                if (((CFXMLProcessingInstructionInfo *)node->additionalData)->dataString) {
163                    CFRelease(((CFXMLProcessingInstructionInfo *)node->additionalData)->dataString);
164                }
165                break;
166            case kCFXMLNodeTypeEntity:
167            {
168                CFXMLEntityInfo *data = (CFXMLEntityInfo *)node->additionalData;
169                if (data->replacementText) CFRelease(data->replacementText);
170                if (data->entityID.systemID) CFRelease(data->entityID.systemID);
171                if (data->entityID.publicID) CFRelease(data->entityID.publicID);
172                if (data->notationName) CFRelease(data->notationName);
173                break;
174            }
175            case kCFXMLNodeTypeEntityReference:
176            {
177                // Do nothing; additionalData has no structure of its own, with dependent pieces to release.  -- REW, 2/11/2000
178                break;
179            }
180            case kCFXMLNodeTypeDocumentType:
181            case kCFXMLNodeTypeNotation:
182                // We get away with this because CFXMLNotationInfo and CFXMLDocumentTypeInfo have identical formats
183            {
184                CFXMLNotationInfo *data = (CFXMLNotationInfo *)node->additionalData;
185                if (data->externalID.systemID) CFRelease(data->externalID.systemID);
186                if (data->externalID.publicID) CFRelease(data->externalID.publicID);
187                break;
188            }
189            case kCFXMLNodeTypeElementTypeDeclaration:
190                if (((CFXMLElementTypeDeclarationInfo *)node->additionalData)->contentDescription) {
191                    CFRelease(((CFXMLElementTypeDeclarationInfo *)node->additionalData)->contentDescription);
192                }
193                break;
194            case kCFXMLNodeTypeAttributeListDeclaration:
195            {
196                CFXMLAttributeListDeclarationInfo *data = (CFXMLAttributeListDeclarationInfo *)node->additionalData;
197                CFIndex idx;
198                for (idx = 0; idx < data->numberOfAttributes; idx ++) {
199                    CFRelease(data->attributes[idx].attributeName);
200                    CFRelease(data->attributes[idx].typeString);
201                    CFRelease(data->attributes[idx].defaultString);
202                }
203                CFAllocatorDeallocate(CFGetAllocator(node), data->attributes);
204                break;
205            }
206            default:
207                CFAssert1(false, __kCFLogAssertion, "%s(): Encountered unexpected typeID %d (additionalData should be empty)", node->dataTypeID);
208        }
209    }
210}
211
212static CFTypeID __kCFXMLNodeTypeID = _kCFRuntimeNotATypeID;
213
214static const CFRuntimeClass __CFXMLNodeClass = {
215    0,
216    "CFXMLNode",
217    NULL,      // init
218    NULL,      // copy
219    __CFXMLNodeDeallocate,
220    __CFXMLNodeEqual,
221    __CFXMLNodeHash,
222    NULL,      //
223    __CFXMLNodeCopyDescription
224};
225
226static void __CFXMLNodeInitialize(void) {
227    __kCFXMLNodeTypeID = _CFRuntimeRegisterClass(&__CFXMLNodeClass);
228}
229
230CFTypeID CFXMLNodeGetTypeID(void) {
231    if (_kCFRuntimeNotATypeID == __kCFXMLNodeTypeID) __CFXMLNodeInitialize();
232    return __kCFXMLNodeTypeID;
233}
234
235CFXMLNodeRef CFXMLNodeCreateCopy(CFAllocatorRef alloc, CFXMLNodeRef origNode) {
236    return CFXMLNodeCreate(alloc, origNode->dataTypeID, origNode->dataString, origNode->additionalData, origNode->version);
237}
238
239static void _copyAddlDataForType(CFAllocatorRef alloc, CFXMLNodeTypeCode xmlType, const void *src, void *dest) {
240    switch(xmlType) {
241        case kCFXMLNodeTypeDocument: {
242            CFXMLDocumentInfo *srcData = (CFXMLDocumentInfo *)src;
243            CFXMLDocumentInfo *destData = (CFXMLDocumentInfo *)dest;
244            destData->sourceURL = srcData->sourceURL ? (CFURLRef)CFRetain(srcData->sourceURL) : NULL;
245            destData->encoding = srcData->encoding;
246            break;
247        }
248        case kCFXMLNodeTypeElement: {
249            CFXMLElementInfo *srcData = (CFXMLElementInfo *)src;
250            CFXMLElementInfo *destData = (CFXMLElementInfo *)dest;
251            if (srcData->attributes && CFDictionaryGetCount(srcData->attributes) != 0) {
252                destData->attributes =  (CFDictionaryRef)CFPropertyListCreateDeepCopy(alloc, srcData->attributes, kCFPropertyListImmutable);
253                destData->attributeOrder = (CFArrayRef)CFPropertyListCreateDeepCopy(alloc, srcData->attributeOrder, kCFPropertyListImmutable);
254            } else {
255                destData->attributes = NULL;
256                destData->attributeOrder = NULL;
257            }
258            destData->isEmpty = srcData->isEmpty;
259            break;
260        }
261        case kCFXMLNodeTypeProcessingInstruction: {
262            CFXMLProcessingInstructionInfo *srcData = (CFXMLProcessingInstructionInfo *)src;
263            CFXMLProcessingInstructionInfo *destData = (CFXMLProcessingInstructionInfo *)dest;
264            destData->dataString = srcData->dataString ? (CFStringRef)CFStringCreateCopy(alloc, srcData->dataString) : NULL;
265            break;
266        }
267        case kCFXMLNodeTypeEntity:
268        {
269            CFXMLEntityInfo *sourceData = (CFXMLEntityInfo *)src;
270            CFXMLEntityInfo *destData = (CFXMLEntityInfo *)dest;
271            destData->entityType = sourceData->entityType;
272            destData->replacementText = sourceData->replacementText ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->replacementText) : NULL;
273            destData->entityID.systemID = sourceData->entityID.systemID ? (CFURLRef)CFRetain(sourceData->entityID.systemID) : NULL;
274            destData->entityID.publicID = sourceData->entityID.publicID ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->entityID.publicID) : NULL;
275            destData->notationName = sourceData->notationName ? (CFStringRef)CFStringCreateCopy(alloc, sourceData->notationName) : NULL;
276            break;
277        }
278        case kCFXMLNodeTypeEntityReference:
279        {
280            CFXMLEntityReferenceInfo *srcData = (CFXMLEntityReferenceInfo *)src;
281            CFXMLEntityReferenceInfo *destData = (CFXMLEntityReferenceInfo *)dest;
282            destData->entityType = srcData->entityType;
283            break;
284        }
285        case kCFXMLNodeTypeDocumentType:
286        case kCFXMLNodeTypeNotation:
287        {
288            // We can get away with this because the structures of CFXMLNotationInfo and CFXMLDocumentTypeInfo match.  -- REW, 3/8/2000
289            CFXMLNotationInfo *srcData = (CFXMLNotationInfo *)src;
290            CFXMLNotationInfo *destData = (CFXMLNotationInfo *)dest;
291            destData->externalID.systemID = srcData->externalID.systemID ? (CFURLRef)CFRetain(srcData->externalID.systemID) : NULL;
292            destData->externalID.publicID = srcData->externalID.publicID ? (CFStringRef)CFStringCreateCopy(alloc, srcData->externalID.publicID) : NULL;
293            break;
294        }
295        case kCFXMLNodeTypeElementTypeDeclaration: {
296            CFXMLElementTypeDeclarationInfo *srcData = (CFXMLElementTypeDeclarationInfo *)src;
297            CFXMLElementTypeDeclarationInfo *destData = (CFXMLElementTypeDeclarationInfo *)dest;
298            destData->contentDescription = srcData->contentDescription ? (CFStringRef)CFStringCreateCopy(alloc, srcData->contentDescription) : NULL;
299            break;
300        }
301        case kCFXMLNodeTypeAttributeListDeclaration:
302        {
303            CFXMLAttributeListDeclarationInfo *sourceData = (CFXMLAttributeListDeclarationInfo *)src;
304            CFXMLAttributeListDeclarationInfo *destData = (CFXMLAttributeListDeclarationInfo *)dest;
305            CFIndex idx;
306            destData->numberOfAttributes = sourceData->numberOfAttributes;
307            destData->attributes = sourceData->numberOfAttributes ? (CFXMLAttributeDeclarationInfo *)CFAllocatorAllocate(alloc, sizeof(CFXMLAttributeDeclarationInfo)*sourceData->numberOfAttributes, 0) : NULL;
308            for (idx = 0; idx < sourceData->numberOfAttributes; idx ++) {
309                CFXMLAttributeDeclarationInfo sourceAttr = sourceData->attributes[idx];
310                CFXMLAttributeDeclarationInfo *destAttr = &(destData->attributes[idx]);
311                destAttr->attributeName = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.attributeName);
312                destAttr->typeString = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.typeString);
313                destAttr->defaultString = (CFStringRef)CFStringCreateCopy(alloc, sourceAttr.defaultString);
314            }
315            break;
316        }
317        default:
318            CFAssert2(false, __kCFLogAssertion, "%s(): Encountered unexpected typeID %d (additionalData should be empty)", __PRETTY_FUNCTION__, xmlType);
319    }
320}
321
322// Designated initializer; all node creation (except the wonky one created by the parser) should happen here.
323CFXMLNodeRef CFXMLNodeCreate(CFAllocatorRef alloc, CFXMLNodeTypeCode xmlType, CFStringRef dataString, const void *additionalData, CFIndex version) {
324    struct __CFXMLNode *node;
325    UInt32 extraSize;
326    // Add assertions checking xmlType against the presence/absence of additionalData & dataString
327    switch (xmlType) {
328        case kCFXMLNodeTypeDocument: extraSize = sizeof(CFXMLDocumentInfo); break;
329        case kCFXMLNodeTypeElement: extraSize = sizeof(CFXMLElementInfo); break;
330        case kCFXMLNodeTypeProcessingInstruction: extraSize = sizeof(CFXMLProcessingInstructionInfo); break;
331        case kCFXMLNodeTypeEntity: extraSize = sizeof(CFXMLEntityInfo); break;
332        case kCFXMLNodeTypeEntityReference: extraSize = sizeof(CFXMLEntityReferenceInfo); break;
333        case kCFXMLNodeTypeDocumentType: extraSize = sizeof(CFXMLDocumentTypeInfo); break;
334        case kCFXMLNodeTypeNotation: extraSize = sizeof(CFXMLNotationInfo); break;
335        case kCFXMLNodeTypeElementTypeDeclaration: extraSize = sizeof(CFXMLElementTypeDeclarationInfo); break;
336        case kCFXMLNodeTypeAttributeListDeclaration: extraSize = sizeof(CFXMLAttributeListDeclarationInfo); break;
337        default: extraSize = 0;
338    }
339
340    node = (struct __CFXMLNode *)_CFRuntimeCreateInstance(alloc, CFXMLNodeGetTypeID(), sizeof(struct __CFXMLNode) + extraSize - sizeof(CFRuntimeBase), NULL);
341    if (node) {
342        alloc = CFGetAllocator(node);
343        node->version = version;
344        node->dataTypeID = xmlType;
345        node->dataString = dataString ? (CFStringRef)CFStringCreateCopy(alloc, dataString) : NULL;
346        if (extraSize != 0) {
347            node->additionalData = (void *)((uint8_t *)node + sizeof(struct __CFXMLNode));
348            _copyAddlDataForType(alloc, xmlType, additionalData, node->additionalData);
349        } else {
350            node->additionalData = NULL;
351        }
352    }
353    return node;
354}
355
356CFXMLNodeTypeCode CFXMLNodeGetTypeCode(CFXMLNodeRef node) {
357    return node->dataTypeID;
358}
359
360CFStringRef CFXMLNodeGetString(CFXMLNodeRef node) {
361    return node->dataString;
362}
363
364const void *CFXMLNodeGetInfoPtr(CFXMLNodeRef node) {
365    return node->additionalData;
366}
367
368CFIndex CFXMLNodeGetVersion(CFXMLNodeRef node) {
369    return node->version;
370}
371
372
373