1 /* 2 * Copyright (c) 2011-2015, 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 "AlwaysAssert.hpp" 33 #include <libxml/tree.h> 34 #include <libxml/xmlschemas.h> 35 #include <libxml/parser.h> 36 #include <libxml/xinclude.h> 37 #include <libxml/uri.h> 38 #include <memory> 39 #include <stdexcept> 40 41 using std::string; 42 using xml_unique_ptr = std::unique_ptr<xmlChar, decltype(xmlFree)>; 43 44 CXmlDocSource::CXmlDocSource(_xmlDoc *pDoc, bool bValidateWithSchema, _xmlNode *pRootNode) 45 : _pDoc(pDoc), _pRootNode(pRootNode), _strRootElementType(""), _strRootElementName(""), 46 _strNameAttributeName(""), _bValidateWithSchema(bValidateWithSchema) 47 { 48 } 49 50 CXmlDocSource::CXmlDocSource(_xmlDoc *pDoc, bool bValidateWithSchema, 51 const string &strRootElementType, const string &strRootElementName, 52 const string &strNameAttributeName) 53 : _pDoc(pDoc), _pRootNode(xmlDocGetRootElement(pDoc)), _strRootElementType(strRootElementType), 54 _strRootElementName(strRootElementName), _strNameAttributeName(strNameAttributeName), 55 _bValidateWithSchema(bValidateWithSchema) 56 { 57 } 58 59 CXmlDocSource::~CXmlDocSource() 60 { 61 if (_pDoc) { 62 // Free XML doc 63 xmlFreeDoc(_pDoc); 64 _pDoc = NULL; 65 } 66 } 67 68 void CXmlDocSource::getRootElement(CXmlElement &xmlRootElement) const 69 { 70 xmlRootElement.setXmlElement(_pRootNode); 71 } 72 73 string CXmlDocSource::getRootElementName() const 74 { 75 return (const char *)_pRootNode->name; 76 } 77 78 string CXmlDocSource::getRootElementAttributeString(const string &strAttributeName) const 79 { 80 CXmlElement topMostElement(_pRootNode); 81 82 string attribute; 83 topMostElement.getAttribute(strAttributeName, attribute); 84 return attribute; 85 } 86 87 void CXmlDocSource::setSchemaBaseUri(const string &uri) 88 { 89 _schemaBaseUri = uri; 90 } 91 92 string CXmlDocSource::getSchemaBaseUri() 93 { 94 return _schemaBaseUri; 95 } 96 97 string CXmlDocSource::getSchemaUri() const 98 { 99 // Adding a trailing '/' is a bit dirty but works fine on both Linux and 100 // Windows in order to make sure that libxml2's URI handling methods 101 // interpret the base URI as a folder. 102 return mkUri(_schemaBaseUri + "/", getRootElementName() + ".xsd"); 103 } 104 105 _xmlDoc *CXmlDocSource::getDoc() const 106 { 107 return _pDoc; 108 } 109 110 bool CXmlDocSource::isParsable() const 111 { 112 // Check that the doc has been created 113 return _pDoc != NULL; 114 } 115 116 bool CXmlDocSource::populate(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 if (!isInstanceDocumentValid()) { 129 130 serializingContext.setError("Document is not valid"); 131 132 return false; 133 } 134 } 135 136 // Check Root element type 137 if (getRootElementName() != _strRootElementType) { 138 139 serializingContext.setError("Error: Wrong XML structure document "); 140 serializingContext.appendLineToError("Root Element " + getRootElementName() + 141 " mismatches expected type " + _strRootElementType); 142 143 return false; 144 } 145 146 if (!_strNameAttributeName.empty()) { 147 148 string strRootElementNameCheck = getRootElementAttributeString(_strNameAttributeName); 149 150 // Check Root element name attribute (if any) 151 if (!_strRootElementName.empty() && strRootElementNameCheck != _strRootElementName) { 152 153 serializingContext.setError("Error: Wrong XML structure document "); 154 serializingContext.appendLineToError( 155 _strRootElementType + " element " + _strRootElementName + " mismatches expected " + 156 _strRootElementType + " type " + strRootElementNameCheck); 157 158 return false; 159 } 160 } 161 162 return true; 163 } 164 165 bool CXmlDocSource::isInstanceDocumentValid() 166 { 167 #ifdef LIBXML_SCHEMAS_ENABLED 168 string schemaUri = getSchemaUri(); 169 170 xmlDocPtr pSchemaDoc = xmlReadFile(schemaUri.c_str(), NULL, XML_PARSE_NONET); 171 172 if (!pSchemaDoc) { 173 // Unable to load Schema 174 return false; 175 } 176 177 xmlSchemaParserCtxtPtr pParserCtxt = xmlSchemaNewDocParserCtxt(pSchemaDoc); 178 179 if (!pParserCtxt) { 180 181 // Unable to create schema context 182 xmlFreeDoc(pSchemaDoc); 183 return false; 184 } 185 186 // Get Schema 187 xmlSchemaPtr pSchema = xmlSchemaParse(pParserCtxt); 188 189 if (!pSchema) { 190 191 // Invalid Schema 192 xmlSchemaFreeParserCtxt(pParserCtxt); 193 xmlFreeDoc(pSchemaDoc); 194 return false; 195 } 196 xmlSchemaValidCtxtPtr pValidationCtxt = xmlSchemaNewValidCtxt(pSchema); 197 198 if (!pValidationCtxt) { 199 200 // Unable to create validation context 201 xmlSchemaFree(pSchema); 202 xmlSchemaFreeParserCtxt(pParserCtxt); 203 xmlFreeDoc(pSchemaDoc); 204 return false; 205 } 206 207 bool isDocValid = xmlSchemaValidateDoc(pValidationCtxt, _pDoc) == 0; 208 209 xmlSchemaFreeValidCtxt(pValidationCtxt); 210 xmlSchemaFree(pSchema); 211 xmlSchemaFreeParserCtxt(pParserCtxt); 212 xmlFreeDoc(pSchemaDoc); 213 214 return isDocValid; 215 #else 216 return true; 217 #endif 218 } 219 220 std::string CXmlDocSource::mkUri(const std::string &base, const std::string &relative) 221 { 222 xml_unique_ptr baseUri(xmlPathToURI((const xmlChar *)base.c_str()), xmlFree); 223 xml_unique_ptr relativeUri(xmlPathToURI((const xmlChar *)relative.c_str()), xmlFree); 224 /* return null pointer if baseUri or relativeUri are null pointer */ 225 xml_unique_ptr xmlUri(xmlBuildURI(relativeUri.get(), baseUri.get()), xmlFree); 226 227 ALWAYS_ASSERT(xmlUri != nullptr, "unable to make URI from: \"" << base << "\" and \"" 228 << relative << "\""); 229 230 return (const char *)xmlUri.get(); 231 } 232 233 _xmlDoc *CXmlDocSource::mkXmlDoc(const string &source, bool fromFile, bool xincludes, 234 CXmlSerializingContext &serializingContext) 235 { 236 _xmlDoc *doc = NULL; 237 if (fromFile) { 238 doc = xmlReadFile(source.c_str(), NULL, 0); 239 } else { 240 doc = xmlReadMemory(source.c_str(), (int)source.size(), "", NULL, 0); 241 } 242 243 if (doc == NULL) { 244 string errorMsg = "libxml failed to read"; 245 if (fromFile) { 246 errorMsg += " \"" + source + "\""; 247 } 248 serializingContext.appendLineToError(errorMsg); 249 250 return NULL; 251 } 252 253 if (xincludes and (xmlXIncludeProcess(doc) < 0)) { 254 serializingContext.appendLineToError("libxml failed to resolve XIncludes"); 255 256 xmlFreeDoc(doc); 257 doc = NULL; 258 } 259 260 return doc; 261 } 262