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
39 using std::string;
40
41 // Schedule for libxml2 library
42 bool CXmlDocSource::_bLibXml2CleanupScheduled;
43
CXmlDocSource(_xmlDoc * pDoc,bool bValidateWithSchema,_xmlNode * pRootNode)44 CXmlDocSource::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
CXmlDocSource(_xmlDoc * pDoc,bool bValidateWithSchema,const string & strXmlSchemaFile,const string & strRootElementType,const string & strRootElementName,const string & strNameAttributeName)57 CXmlDocSource::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
~CXmlDocSource()73 CXmlDocSource::~CXmlDocSource()
74 {
75 if (_pDoc) {
76 // Free XML doc
77 xmlFreeDoc(_pDoc);
78 _pDoc = NULL;
79 }
80 }
81
getRootElement(CXmlElement & xmlRootElement) const82 void CXmlDocSource::getRootElement(CXmlElement& xmlRootElement) const
83 {
84 xmlRootElement.setXmlElement(_pRootNode);
85 }
86
getRootElementName() const87 string CXmlDocSource::getRootElementName() const
88 {
89 return (const char*)_pRootNode->name;
90 }
91
getRootElementAttributeString(const string & strAttributeName) const92 string CXmlDocSource::getRootElementAttributeString(const string& strAttributeName) const
93 {
94 CXmlElement topMostElement(_pRootNode);
95
96 return topMostElement.getAttributeString(strAttributeName);
97 }
98
getDoc() const99 _xmlDoc* CXmlDocSource::getDoc() const
100 {
101 return _pDoc;
102 }
103
isParsable() const104 bool CXmlDocSource::isParsable() const
105 {
106 // Check that the doc has been created
107 return _pDoc != NULL;
108 }
109
populate(CXmlSerializingContext & serializingContext)110 bool CXmlDocSource::populate(CXmlSerializingContext& serializingContext)
111 {
112 return validate(serializingContext);
113
114 }
115
validate(CXmlSerializingContext & serializingContext)116 bool 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
init()167 void CXmlDocSource::init()
168 {
169 if (!_bLibXml2CleanupScheduled) {
170
171 // Schedule cleanup
172 atexit(xmlCleanupParser);
173
174 _bLibXml2CleanupScheduled = true;
175 }
176 }
177
isInstanceDocumentValid()178 bool 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
schemaValidityStructuredErrorFunc(void * pUserData,_xmlError * pError)233 void 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
mkXmlDoc(const string & source,bool fromFile,bool xincludes,string & errorMsg)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