1/*
2 * Copyright (c) 2011-2014, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation and/or
13 * other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "XmlDocSource.h"
32#include <libxml/tree.h>
33#include <libxml/xmlschemas.h>
34#include <libxml/parser.h>
35#include <libxml/xinclude.h>
36#include <libxml/xmlerror.h>
37#include <stdlib.h>
38
39using std::string;
40
41// Schedule for libxml2 library
42bool CXmlDocSource::_bLibXml2CleanupScheduled;
43
44CXmlDocSource::CXmlDocSource(_xmlDoc *pDoc, bool bValidateWithSchema,
45                             _xmlNode *pRootNode) :
46      _pDoc(pDoc),
47      _pRootNode(pRootNode),
48      _strXmlSchemaFile(""),
49      _strRootElementType(""),
50      _strRootElementName(""),
51      _strNameAttributeName(""),
52      _bValidateWithSchema(bValidateWithSchema)
53{
54    init();
55}
56
57CXmlDocSource::CXmlDocSource(_xmlDoc *pDoc, bool bValidateWithSchema,
58                             const string& strXmlSchemaFile,
59                             const string& strRootElementType,
60                             const string& strRootElementName,
61                             const string& strNameAttributeName) :
62    _pDoc(pDoc),
63    _pRootNode(xmlDocGetRootElement(pDoc)),
64    _strXmlSchemaFile(strXmlSchemaFile),
65    _strRootElementType(strRootElementType),
66    _strRootElementName(strRootElementName),
67    _strNameAttributeName(strNameAttributeName),
68    _bValidateWithSchema(bValidateWithSchema)
69{
70    init();
71}
72
73CXmlDocSource::~CXmlDocSource()
74{
75    if (_pDoc) {
76        // Free XML doc
77        xmlFreeDoc(_pDoc);
78        _pDoc = NULL;
79    }
80}
81
82void CXmlDocSource::getRootElement(CXmlElement& xmlRootElement) const
83{
84    xmlRootElement.setXmlElement(_pRootNode);
85}
86
87string CXmlDocSource::getRootElementName() const
88{
89    return (const char*)_pRootNode->name;
90}
91
92string CXmlDocSource::getRootElementAttributeString(const string& strAttributeName) const
93{
94    CXmlElement topMostElement(_pRootNode);
95
96    return topMostElement.getAttributeString(strAttributeName);
97}
98
99_xmlDoc* CXmlDocSource::getDoc() const
100{
101    return _pDoc;
102}
103
104bool CXmlDocSource::isParsable() const
105{
106    // Check that the doc has been created
107    return _pDoc != NULL;
108}
109
110bool CXmlDocSource::populate(CXmlSerializingContext& serializingContext)
111{
112    return validate(serializingContext);
113
114}
115
116bool CXmlDocSource::validate(CXmlSerializingContext& serializingContext)
117{
118    // Check that the doc has been created
119    if (!_pDoc) {
120
121        serializingContext.setError("Could not parse document ");
122
123        return false;
124    }
125
126    // Validate if necessary
127    if (_bValidateWithSchema)
128    {
129        if (!isInstanceDocumentValid()) {
130
131            serializingContext.setError("Document is not valid");
132
133            return false;
134        }
135    }
136
137    // Check Root element type
138    if (getRootElementName() != _strRootElementType) {
139
140        serializingContext.setError("Error: Wrong XML structure document ");
141        serializingContext.appendLineToError("Root Element " + getRootElementName()
142                                             + " mismatches expected type " + _strRootElementType);
143
144        return false;
145    }
146
147    if (!_strNameAttributeName.empty()) {
148
149        string strRootElementNameCheck = getRootElementAttributeString(_strNameAttributeName);
150
151        // Check Root element name attribute (if any)
152        if (!_strRootElementName.empty() && strRootElementNameCheck != _strRootElementName) {
153
154            serializingContext.setError("Error: Wrong XML structure document ");
155            serializingContext.appendLineToError(_strRootElementType + " element "
156                                                 + _strRootElementName + " mismatches expected "
157                                                 + _strRootElementType + " type "
158                                                 + strRootElementNameCheck);
159
160            return false;
161        }
162    }
163
164    return true;
165}
166
167void CXmlDocSource::init()
168{
169    if (!_bLibXml2CleanupScheduled) {
170
171        // Schedule cleanup
172        atexit(xmlCleanupParser);
173
174        _bLibXml2CleanupScheduled = true;
175    }
176}
177
178bool CXmlDocSource::isInstanceDocumentValid()
179{
180#ifdef LIBXML_SCHEMAS_ENABLED
181    xmlDocPtr pSchemaDoc = xmlReadFile(_strXmlSchemaFile.c_str(), NULL, XML_PARSE_NONET);
182
183    if (!pSchemaDoc) {
184        // Unable to load Schema
185        return false;
186    }
187
188    xmlSchemaParserCtxtPtr pParserCtxt = xmlSchemaNewDocParserCtxt(pSchemaDoc);
189
190    if (!pParserCtxt) {
191
192        // Unable to create schema context
193        xmlFreeDoc(pSchemaDoc);
194        return false;
195    }
196
197    // Get Schema
198    xmlSchemaPtr pSchema = xmlSchemaParse(pParserCtxt);
199
200    if (!pSchema) {
201
202        // Invalid Schema
203        xmlSchemaFreeParserCtxt(pParserCtxt);
204        xmlFreeDoc(pSchemaDoc);
205        return false;
206    }
207    xmlSchemaValidCtxtPtr pValidationCtxt = xmlSchemaNewValidCtxt(pSchema);
208
209    if (!pValidationCtxt) {
210
211        // Unable to create validation context
212        xmlSchemaFree(pSchema);
213        xmlSchemaFreeParserCtxt(pParserCtxt);
214        xmlFreeDoc(pSchemaDoc);
215        return false;
216    }
217
218    xmlSetStructuredErrorFunc(this, schemaValidityStructuredErrorFunc);
219
220    bool isDocValid = xmlSchemaValidateDoc(pValidationCtxt, _pDoc) == 0;
221
222    xmlSchemaFreeValidCtxt(pValidationCtxt);
223    xmlSchemaFree(pSchema);
224    xmlSchemaFreeParserCtxt(pParserCtxt);
225    xmlFreeDoc(pSchemaDoc);
226
227    return isDocValid;
228#else
229    return true;
230#endif
231}
232
233void CXmlDocSource::schemaValidityStructuredErrorFunc(void* pUserData, _xmlError* pError)
234{
235    (void)pUserData;
236
237#ifdef LIBXML_SCHEMAS_ENABLED
238    // Display message
239    puts(pError->message);
240#endif
241}
242
243_xmlDoc* CXmlDocSource::mkXmlDoc(const string& source, bool fromFile, bool xincludes, string& errorMsg)
244{
245    _xmlDoc* doc = NULL;
246    if (fromFile) {
247        doc = xmlReadFile(source.c_str(), NULL, 0);
248    } else {
249        doc = xmlReadMemory(source.c_str(), (int)source.size(), "", NULL, 0);
250    }
251
252    if (doc == NULL) {
253        errorMsg = "libxml failed to read";
254        if (fromFile) {
255            errorMsg += " \"" + source + "\"";
256        }
257
258        xmlError* details = xmlGetLastError();
259        if (details != NULL) {
260            errorMsg += ": " + string(details->message);
261        }
262
263        return NULL;
264    }
265
266    if (xincludes and (xmlXIncludeProcess(doc) < 0)) {
267        errorMsg = "libxml failed to resolve XIncludes";
268        xmlError* details = xmlGetLastError();
269        if (details != NULL) {
270            errorMsg += ": " + string(details->message);
271        }
272
273        xmlFreeDoc(doc);
274        doc = NULL;
275    }
276
277    return doc;
278}
279