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