1 : /******************************************************************************
2 : * $Id: cpl_xml_validate.cpp 25229 2012-11-16 19:06:58Z rouault $
3 : *
4 : * Project: CPL - Common Portability Library
5 : * Purpose: Implement XML validation against XSD schema
6 : * Author: Even Rouault, even.rouault at mines-paris.org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2012, Even Rouault
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_conv.h"
31 :
32 : CPL_CVSID("$Id: cpl_xml_validate.cpp 25229 2012-11-16 19:06:58Z rouault $");
33 :
34 : #ifdef HAVE_LIBXML2
35 : #include <libxml/xmlversion.h>
36 : #if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20622
37 : /* We need at least 2.6.20 for xmlSchemaValidateDoc */
38 : /* and xmlParseDoc to accept a const xmlChar* */
39 : /* We could workaround it, but likely not worth the effort for now. */
40 : /* Actually, we need at least 2.6.22, at runtime, to be */
41 : /* able to parse the OGC GML schemas */
42 : #define HAVE_RECENT_LIBXML2
43 :
44 : /* libxml2 before 2.8.0 had a bug to parse the OGC GML schemas */
45 : /* We have a workaround for that for versions >= 2.6.20 and < 2.8.0 */
46 : #if defined(LIBXML_VERSION) && LIBXML_VERSION < 20800
47 : #define HAS_VALIDATION_BUG
48 : #endif
49 :
50 : #else
51 : #warning "Not recent enough libxml2 version"
52 : #endif
53 : #endif
54 :
55 : #ifdef HAVE_RECENT_LIBXML2
56 : #include <string.h>
57 : #include <libxml/xmlschemas.h>
58 : #include <libxml/parserInternals.h>
59 : #include <libxml/catalog.h>
60 :
61 : #include "cpl_string.h"
62 : #include "cpl_hash_set.h"
63 : #include "cpl_minixml.h"
64 :
65 : static xmlExternalEntityLoader pfnLibXMLOldExtranerEntityLoader = NULL;
66 :
67 : /************************************************************************/
68 : /* CPLFixPath() */
69 : /************************************************************************/
70 :
71 : /* Replace \ by / to make libxml2 happy on Windows and */
72 : /* replace "a/b/../c" pattern by "a/c" */
73 1725 : static void CPLFixPath(char* pszPath)
74 : {
75 113273 : for(int i=0;pszPath[i] != '\0';i++)
76 : {
77 111548 : if (pszPath[i] == '\\')
78 0 : pszPath[i] = '/';
79 : }
80 :
81 310 : while(TRUE)
82 : {
83 2035 : char* pszSlashDotDot = strstr(pszPath, "/../");
84 2035 : if (pszSlashDotDot == NULL || pszSlashDotDot == pszPath)
85 1725 : return;
86 310 : char* pszSlashBefore = pszSlashDotDot-1;
87 1550 : while(pszSlashBefore > pszPath && *pszSlashBefore != '/')
88 930 : pszSlashBefore --;
89 310 : if (pszSlashBefore == pszPath)
90 0 : return;
91 : memmove(pszSlashBefore + 1, pszSlashDotDot + 4,
92 310 : strlen(pszSlashDotDot + 4) + 1);
93 : }
94 : }
95 :
96 : #ifdef HAS_VALIDATION_BUG
97 :
98 : static int bHasLibXMLBug = -1;
99 :
100 : /************************************************************************/
101 : /* CPLHasLibXMLBugWarningCallback() */
102 : /************************************************************************/
103 :
104 1 : static void CPLHasLibXMLBugWarningCallback (void * ctx, const char * msg, ...)
105 : {
106 1 : }
107 :
108 : /************************************************************************/
109 : /* CPLHasLibXMLBug() */
110 : /************************************************************************/
111 :
112 247 : static int CPLHasLibXMLBug()
113 : {
114 247 : if (bHasLibXMLBug >= 0)
115 246 : return bHasLibXMLBug;
116 :
117 : static const char szLibXMLBugTester[] =
118 : "<schema targetNamespace=\"http://foo\" xmlns:foo=\"http://foo\" xmlns=\"http://www.w3.org/2001/XMLSchema\">"
119 : "<simpleType name=\"t1\">"
120 : "<list itemType=\"double\"/>"
121 : "</simpleType>"
122 : "<complexType name=\"t2\">"
123 : "<simpleContent>"
124 : "<extension base=\"foo:t1\"/>"
125 : "</simpleContent>"
126 : "</complexType>"
127 : "<complexType name=\"t3\">"
128 : "<simpleContent>"
129 : "<restriction base=\"foo:t2\">"
130 : "<length value=\"2\"/>"
131 : "</restriction>"
132 : "</simpleContent>"
133 : "</complexType>"
134 : "</schema>";
135 :
136 : xmlSchemaParserCtxtPtr pSchemaParserCtxt;
137 : xmlSchemaPtr pSchema;
138 :
139 1 : pSchemaParserCtxt = xmlSchemaNewMemParserCtxt(szLibXMLBugTester, strlen(szLibXMLBugTester));
140 :
141 : xmlSchemaSetParserErrors(pSchemaParserCtxt,
142 : CPLHasLibXMLBugWarningCallback,
143 : CPLHasLibXMLBugWarningCallback,
144 1 : NULL);
145 :
146 1 : pSchema = xmlSchemaParse(pSchemaParserCtxt);
147 1 : xmlSchemaFreeParserCtxt(pSchemaParserCtxt);
148 :
149 1 : bHasLibXMLBug = (pSchema == NULL);
150 :
151 1 : if (pSchema)
152 0 : xmlSchemaFree(pSchema);
153 :
154 1 : if (bHasLibXMLBug)
155 : {
156 : CPLDebug("CPL",
157 : "LibXML bug found (cf https://bugzilla.gnome.org/show_bug.cgi?id=630130). "
158 1 : "Will try to workaround for GML schemas.");
159 : }
160 :
161 1 : return bHasLibXMLBug;
162 : }
163 :
164 : #endif
165 :
166 : /************************************************************************/
167 : /* CPLExtractSubSchema() */
168 : /************************************************************************/
169 :
170 1024 : static CPLXMLNode* CPLExtractSubSchema(CPLXMLNode* psSubXML, CPLXMLNode* psMainSchema)
171 : {
172 1024 : if (psSubXML->eType == CXT_Element && strcmp(psSubXML->pszValue, "?xml") == 0)
173 : {
174 1024 : CPLXMLNode* psNext = psSubXML->psNext;
175 1024 : psSubXML->psNext = NULL;
176 1024 : CPLDestroyXMLNode(psSubXML);
177 1024 : psSubXML = psNext;
178 : }
179 :
180 1024 : if (psSubXML != NULL && psSubXML->eType == CXT_Comment)
181 : {
182 64 : CPLXMLNode* psNext = psSubXML->psNext;
183 64 : psSubXML->psNext = NULL;
184 64 : CPLDestroyXMLNode(psSubXML);
185 64 : psSubXML = psNext;
186 : }
187 :
188 1024 : if (psSubXML != NULL && psSubXML->eType == CXT_Element &&
189 : (strcmp(psSubXML->pszValue, "schema") == 0 ||
190 : strcmp(psSubXML->pszValue, "xs:schema") == 0 ||
191 : strcmp(psSubXML->pszValue, "xsd:schema") == 0) &&
192 : psSubXML->psNext == NULL)
193 : {
194 1024 : CPLXMLNode* psNext = psSubXML->psChild;
195 8382 : while(psNext != NULL && psNext->eType != CXT_Element &&
196 : psNext->psNext != NULL && psNext->psNext->eType != CXT_Element)
197 : {
198 : /* Add xmlns: from subschema to main schema if missing */
199 6334 : if (psNext->eType == CXT_Attribute &&
200 : strncmp(psNext->pszValue, "xmlns:", 6) == 0 &&
201 : CPLGetXMLValue(psMainSchema, psNext->pszValue, NULL) == NULL)
202 : {
203 761 : CPLXMLNode* psAttr = CPLCreateXMLNode(NULL, CXT_Attribute, psNext->pszValue);
204 761 : CPLCreateXMLNode(psAttr, CXT_Text, psNext->psChild->pszValue);
205 :
206 761 : psAttr->psNext = psMainSchema->psChild;
207 761 : psMainSchema->psChild = psAttr;
208 : }
209 6334 : psNext = psNext->psNext;
210 : }
211 :
212 1024 : if (psNext != NULL && psNext->eType != CXT_Element &&
213 : psNext->psNext != NULL && psNext->psNext->eType == CXT_Element)
214 : {
215 1024 : CPLXMLNode* psNext2 = psNext->psNext;
216 1024 : psNext->psNext = NULL;
217 1024 : CPLDestroyXMLNode(psSubXML);
218 1024 : psSubXML = psNext2;
219 : }
220 : }
221 :
222 1024 : return psSubXML;
223 : }
224 :
225 : #ifdef HAS_VALIDATION_BUG
226 : /************************************************************************/
227 : /* CPLWorkaroundLibXMLBug() */
228 : /************************************************************************/
229 :
230 : /* Return TRUE if the current node must be destroyed */
231 71694 : static int CPLWorkaroundLibXMLBug(CPLXMLNode* psIter)
232 : {
233 71694 : if (psIter->eType == CXT_Element &&
234 : strcmp(psIter->pszValue, "element") == 0 &&
235 : strcmp(CPLGetXMLValue(psIter, "name", ""), "QuantityExtent") == 0 &&
236 : strcmp(CPLGetXMLValue(psIter, "type", ""), "gml:QuantityExtentType") == 0)
237 : {
238 28 : CPLXMLNode* psIter2 = psIter->psChild;
239 140 : while(psIter2)
240 : {
241 84 : if (psIter2->eType == CXT_Attribute && strcmp(psIter2->pszValue, "type") == 0)
242 : {
243 28 : CPLFree(psIter2->psChild->pszValue);
244 28 : if (strcmp(CPLGetXMLValue(psIter, "substitutionGroup", ""), "gml:AbstractValue") == 0)
245 10 : psIter2->psChild->pszValue = CPLStrdup("gml:MeasureOrNilReasonListType"); /* GML 3.2.1 */
246 : else
247 18 : psIter2->psChild->pszValue = CPLStrdup("gml:MeasureOrNullListType");
248 : }
249 84 : psIter2 = psIter2->psNext;
250 : }
251 : }
252 :
253 71666 : else if (psIter->eType == CXT_Element &&
254 : strcmp(psIter->pszValue, "element") == 0 &&
255 : strcmp(CPLGetXMLValue(psIter, "name", ""), "CategoryExtent") == 0 &&
256 : strcmp(CPLGetXMLValue(psIter, "type", ""), "gml:CategoryExtentType") == 0)
257 : {
258 28 : CPLXMLNode* psIter2 = psIter->psChild;
259 140 : while(psIter2)
260 : {
261 84 : if (psIter2->eType == CXT_Attribute && strcmp(psIter2->pszValue, "type") == 0)
262 : {
263 28 : CPLFree(psIter2->psChild->pszValue);
264 28 : if (strcmp(CPLGetXMLValue(psIter, "substitutionGroup", ""), "gml:AbstractValue") == 0)
265 10 : psIter2->psChild->pszValue = CPLStrdup("gml:CodeOrNilReasonListType"); /* GML 3.2.1 */
266 : else
267 18 : psIter2->psChild->pszValue = CPLStrdup("gml:CodeOrNullListType");
268 : }
269 84 : psIter2 = psIter2->psNext;
270 : }
271 : }
272 :
273 71638 : else if (bHasLibXMLBug && psIter->eType == CXT_Element &&
274 : strcmp(psIter->pszValue, "complexType") == 0 &&
275 : (strcmp(CPLGetXMLValue(psIter, "name", ""), "QuantityExtentType") == 0 ||
276 : strcmp(CPLGetXMLValue(psIter, "name", ""), "CategoryExtentType") == 0))
277 : {
278 : /* Destroy this element */
279 56 : return TRUE;
280 : }
281 :
282 : /* For GML 3.2.1 */
283 71582 : else if (psIter->eType == CXT_Element &&
284 : strcmp(psIter->pszValue, "complexType") == 0 &&
285 : strcmp(CPLGetXMLValue(psIter, "name", ""), "VectorType") == 0)
286 : {
287 28 : CPLXMLNode* psSimpleContent = CPLCreateXMLNode(NULL, CXT_Element, "simpleContent");
288 28 : CPLXMLNode* psExtension = CPLCreateXMLNode(psSimpleContent, CXT_Element, "extension");
289 28 : CPLXMLNode* psExtensionBase = CPLCreateXMLNode(psExtension, CXT_Attribute, "base");
290 28 : CPLCreateXMLNode(psExtensionBase, CXT_Text, "gml:doubleList");
291 28 : CPLXMLNode* psAttributeGroup = CPLCreateXMLNode(psExtension, CXT_Element, "attributeGroup");
292 28 : CPLXMLNode* psAttributeGroupRef = CPLCreateXMLNode(psAttributeGroup, CXT_Attribute, "ref");
293 28 : CPLCreateXMLNode(psAttributeGroupRef, CXT_Text, "gml:SRSReferenceGroup");
294 :
295 28 : CPLXMLNode* psName = CPLCreateXMLNode(NULL, CXT_Attribute, "name");
296 28 : CPLCreateXMLNode(psName, CXT_Text, "VectorType");
297 :
298 28 : CPLDestroyXMLNode(psIter->psChild);
299 28 : psIter->psChild = psName;
300 28 : psIter->psChild->psNext = psSimpleContent;
301 : }
302 :
303 71554 : else if (psIter->eType == CXT_Element &&
304 : strcmp(psIter->pszValue, "element") == 0 &&
305 : (strcmp(CPLGetXMLValue(psIter, "name", ""), "domainOfValidity") == 0 ||
306 : strcmp(CPLGetXMLValue(psIter, "name", ""), "coordinateOperationAccuracy") == 0 ||
307 : strcmp(CPLGetXMLValue(psIter, "name", ""), "formulaCitation") == 0))
308 : {
309 30 : CPLXMLNode* psComplexType = CPLCreateXMLNode(NULL, CXT_Element, "complexType");
310 30 : CPLXMLNode* psSequence = CPLCreateXMLNode(psComplexType, CXT_Element, "sequence");
311 30 : CPLXMLNode* psSequenceMinOccurs = CPLCreateXMLNode(psSequence, CXT_Attribute, "minOccurs");
312 30 : CPLCreateXMLNode(psSequenceMinOccurs, CXT_Text, "0");
313 30 : CPLXMLNode* psAny = CPLCreateXMLNode(psSequence, CXT_Element, "any");
314 30 : CPLXMLNode* psAnyMinOccurs = CPLCreateXMLNode(psAny, CXT_Attribute, "minOccurs");
315 30 : CPLCreateXMLNode(psAnyMinOccurs, CXT_Text, "0");
316 30 : CPLXMLNode* psAnyProcessContents = CPLCreateXMLNode(psAny, CXT_Attribute, " processContents");
317 30 : CPLCreateXMLNode(psAnyProcessContents, CXT_Text, "lax");
318 :
319 30 : CPLXMLNode* psName = CPLCreateXMLNode(NULL, CXT_Attribute, "name");
320 30 : CPLCreateXMLNode(psName, CXT_Text, CPLGetXMLValue(psIter, "name", ""));
321 :
322 30 : CPLDestroyXMLNode(psIter->psChild);
323 30 : psIter->psChild = psName;
324 30 : psIter->psChild->psNext = psComplexType;
325 : }
326 :
327 71638 : return FALSE;
328 : }
329 : #endif
330 :
331 : /************************************************************************/
332 : /* CPLLoadSchemaStrInternal() */
333 : /************************************************************************/
334 :
335 : static
336 1271 : CPLXMLNode* CPLLoadSchemaStrInternal(CPLHashSet* hSetSchemas,
337 : const char* pszFile)
338 : {
339 : CPLXMLNode* psXML;
340 : CPLXMLNode* psSchema;
341 : CPLXMLNode* psPrev;
342 : CPLXMLNode* psIter;
343 :
344 1271 : if (CPLHashSetLookup(hSetSchemas, pszFile))
345 0 : return NULL;
346 :
347 1271 : CPLHashSetInsert(hSetSchemas, CPLStrdup(pszFile));
348 :
349 1271 : CPLDebug("CPL", "Parsing %s", pszFile);
350 :
351 1271 : psXML = CPLParseXMLFile(pszFile);
352 1271 : if (psXML == NULL)
353 : {
354 : CPLError(CE_Failure, CPLE_AppDefined,
355 0 : "Cannot open %s", pszFile);
356 0 : return NULL;
357 : }
358 :
359 1271 : psSchema = CPLGetXMLNode(psXML, "=schema");
360 1271 : if (psSchema == NULL)
361 315 : psSchema = CPLGetXMLNode(psXML, "=xs:schema");
362 1271 : if (psSchema == NULL)
363 17 : psSchema = CPLGetXMLNode(psXML, "=xsd:schema");
364 1271 : if (psSchema == NULL)
365 : {
366 : CPLError(CE_Failure, CPLE_AppDefined,
367 0 : "Cannot find schema node in %s", pszFile);
368 0 : CPLDestroyXMLNode(psXML);
369 0 : return NULL;
370 : }
371 :
372 1271 : psPrev = NULL;
373 1271 : psIter = psSchema->psChild;
374 74236 : while(psIter)
375 : {
376 71694 : int bDestroyCurrentNode = FALSE;
377 :
378 : #ifdef HAS_VALIDATION_BUG
379 71694 : if (bHasLibXMLBug)
380 71694 : bDestroyCurrentNode = CPLWorkaroundLibXMLBug(psIter);
381 : #endif
382 :
383 : /* Load the referenced schemas, and integrate them in the main schema */
384 72341 : if (psIter->eType == CXT_Element &&
385 : (strcmp(psIter->pszValue, "include") == 0 ||
386 : strcmp(psIter->pszValue, "xs:include") == 0||
387 : strcmp(psIter->pszValue, "xsd:include") == 0) &&
388 : psIter->psChild != NULL &&
389 : psIter->psChild->eType == CXT_Attribute &&
390 : strcmp(psIter->psChild->pszValue, "schemaLocation") == 0)
391 : {
392 1671 : const char* pszIncludeSchema = psIter->psChild->psChild->pszValue;
393 : char* pszFullFilename = CPLStrdup(
394 1671 : CPLFormFilename(CPLGetPath(pszFile), pszIncludeSchema, NULL));
395 :
396 1671 : CPLFixPath(pszFullFilename);
397 :
398 1671 : CPLXMLNode* psSubXML = NULL;
399 :
400 : /* If we haven't yet loaded that schema, do it now */
401 1671 : if (!CPLHashSetLookup(hSetSchemas, pszFullFilename))
402 : {
403 1024 : psSubXML = CPLLoadSchemaStrInternal(hSetSchemas, pszFullFilename);
404 1024 : if (psSubXML == NULL)
405 : {
406 0 : CPLFree(pszFullFilename);
407 0 : CPLDestroyXMLNode(psXML);
408 0 : return NULL;
409 : }
410 : }
411 1671 : CPLFree(pszFullFilename);
412 1671 : pszFullFilename = NULL;
413 :
414 1671 : if (psSubXML)
415 : {
416 1024 : CPLXMLNode* psNext = psIter->psNext;
417 :
418 1024 : psSubXML = CPLExtractSubSchema(psSubXML, psSchema);
419 1024 : if (psSubXML == NULL)
420 : {
421 0 : CPLDestroyXMLNode(psXML);
422 0 : return NULL;
423 : }
424 :
425 : /* Replace <include/> node by the subXML */
426 1024 : CPLXMLNode* psIter2 = psSubXML;
427 165928 : while(psIter2->psNext)
428 163880 : psIter2 = psIter2->psNext;
429 1024 : psIter2->psNext = psNext;
430 :
431 1024 : if (psPrev == NULL)
432 0 : psSchema->psChild = psSubXML;
433 : else
434 1024 : psPrev->psNext = psSubXML;
435 :
436 1024 : psIter->psNext = NULL;
437 1024 : CPLDestroyXMLNode(psIter);
438 :
439 1024 : psPrev = psIter2;
440 1024 : psIter = psNext;
441 1024 : continue;
442 : }
443 : else
444 : {
445 : /* We have already included that file, */
446 : /* so just remove the <include/> node */
447 647 : bDestroyCurrentNode = TRUE;
448 : }
449 : }
450 :
451 : /* Patch the schemaLocation of <import/> */
452 70023 : else if (psIter->eType == CXT_Element &&
453 : (strcmp(psIter->pszValue, "import") == 0 ||
454 : strcmp(psIter->pszValue, "xs:import") == 0||
455 : strcmp(psIter->pszValue, "xsd:import") == 0))
456 : {
457 535 : CPLXMLNode* psIter2 = psIter->psChild;
458 2140 : while(psIter2)
459 : {
460 1070 : if (psIter2->eType == CXT_Attribute &&
461 : strcmp(psIter2->pszValue, "schemaLocation") == 0 &&
462 : psIter2->psChild != NULL &&
463 : strncmp(psIter2->psChild->pszValue, "http://", 7) != 0 &&
464 : strncmp(psIter2->psChild->pszValue, "ftp://", 6) != 0 &&
465 : /* If the top file is our warping file, don't alter the path of the import */
466 : strstr(pszFile, "/vsimem/CPLValidateXML_") == NULL )
467 : {
468 : char* pszFullFilename = CPLStrdup(CPLFormFilename(
469 54 : CPLGetPath(pszFile), psIter2->psChild->pszValue, NULL));
470 54 : CPLFixPath(pszFullFilename);
471 54 : CPLFree(psIter2->psChild->pszValue);
472 54 : psIter2->psChild->pszValue = pszFullFilename;
473 : }
474 1070 : psIter2 = psIter2->psNext;
475 : }
476 : }
477 :
478 70670 : if (bDestroyCurrentNode)
479 : {
480 703 : CPLXMLNode* psNext = psIter->psNext;
481 703 : if (psPrev == NULL)
482 0 : psSchema->psChild = psNext;
483 : else
484 703 : psPrev->psNext = psNext;
485 :
486 703 : psIter->psNext = NULL;
487 703 : CPLDestroyXMLNode(psIter);
488 :
489 703 : psIter = psNext;
490 703 : continue;
491 : }
492 :
493 69967 : psPrev = psIter;
494 69967 : psIter = psIter->psNext;
495 : }
496 :
497 1271 : return psXML;
498 : }
499 :
500 : /************************************************************************/
501 : /* CPLMoveImportAtBeginning() */
502 : /************************************************************************/
503 :
504 : static
505 247 : void CPLMoveImportAtBeginning(CPLXMLNode* psXML)
506 : {
507 : CPLXMLNode* psIter;
508 : CPLXMLNode* psPrev;
509 : CPLXMLNode* psSchema;
510 :
511 247 : psSchema = CPLGetXMLNode(psXML, "=schema");
512 247 : if (psSchema == NULL)
513 96 : psSchema = CPLGetXMLNode(psXML, "=xs:schema");
514 247 : if (psSchema == NULL)
515 8 : psSchema = CPLGetXMLNode(psXML, "=xsd:schema");
516 247 : if (psSchema == NULL)
517 0 : return;
518 :
519 247 : psPrev = NULL;
520 247 : psIter = psSchema->psChild;
521 63864 : while(psIter)
522 : {
523 63370 : if (psPrev != NULL && psIter->eType == CXT_Element &&
524 : (strcmp(psIter->pszValue, "import") == 0 ||
525 : strcmp(psIter->pszValue, "xs:import") == 0 ||
526 : strcmp(psIter->pszValue, "xsd:import") == 0))
527 : {
528 : /* Reorder at the beginning */
529 535 : CPLXMLNode* psNext = psIter->psNext;
530 :
531 535 : psPrev->psNext = psNext;
532 :
533 535 : CPLXMLNode* psFirstChild = psSchema->psChild;
534 535 : psSchema->psChild = psIter;
535 535 : psIter->psNext = psFirstChild;
536 :
537 535 : psIter = psNext;
538 535 : continue;
539 : }
540 :
541 62835 : psPrev = psIter;
542 62835 : psIter = psIter->psNext;
543 : }
544 : }
545 :
546 : /************************************************************************/
547 : /* CPLLoadSchemaStr() */
548 : /************************************************************************/
549 :
550 : static
551 247 : char* CPLLoadSchemaStr(const char* pszXSDFilename)
552 : {
553 247 : char* pszStr = NULL;
554 :
555 : #ifdef HAS_VALIDATION_BUG
556 247 : CPLHasLibXMLBug();
557 : #endif
558 :
559 : CPLHashSet* hSetSchemas =
560 247 : CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
561 : CPLXMLNode* psSchema =
562 247 : CPLLoadSchemaStrInternal(hSetSchemas, pszXSDFilename);
563 247 : if (psSchema)
564 : {
565 247 : CPLMoveImportAtBeginning(psSchema);
566 247 : pszStr = CPLSerializeXMLTree(psSchema);
567 247 : CPLDestroyXMLNode(psSchema);
568 : }
569 247 : CPLHashSetDestroy(hSetSchemas);
570 247 : return pszStr;
571 : }
572 :
573 : /************************************************************************/
574 : /* CPLLibXMLInputStreamCPLFree() */
575 : /************************************************************************/
576 :
577 209 : static void CPLLibXMLInputStreamCPLFree(xmlChar* pszBuffer)
578 : {
579 209 : CPLFree(pszBuffer);
580 209 : }
581 :
582 : /************************************************************************/
583 : /* CPLFindLocalXSD() */
584 : /************************************************************************/
585 :
586 1 : static CPLString CPLFindLocalXSD(const char* pszXSDFilename)
587 : {
588 : const char *pszSchemasOpenGIS;
589 1 : CPLString osTmp;
590 :
591 1 : pszSchemasOpenGIS = CPLGetConfigOption("GDAL_OPENGIS_SCHEMAS", NULL);
592 1 : if (pszSchemasOpenGIS != NULL)
593 : {
594 1 : int nLen = (int)strlen(pszSchemasOpenGIS);
595 1 : if (nLen > 0 && pszSchemasOpenGIS[nLen-1] == '/')
596 : {
597 0 : osTmp = pszSchemasOpenGIS;
598 0 : osTmp += pszXSDFilename;
599 : }
600 : else
601 : {
602 1 : osTmp = pszSchemasOpenGIS;
603 1 : osTmp += "/";
604 1 : osTmp += pszXSDFilename;
605 : }
606 : }
607 0 : else if ((pszSchemasOpenGIS = CPLFindFile( "gdal", "SCHEMAS_OPENGIS_NET" )) != NULL)
608 : {
609 0 : osTmp = pszSchemasOpenGIS;
610 0 : osTmp += "/";
611 0 : osTmp += pszXSDFilename;
612 : }
613 :
614 : VSIStatBufL sStatBuf;
615 1 : if( VSIStatExL(osTmp, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 )
616 0 : return osTmp;
617 1 : return "";
618 : }
619 :
620 : /************************************************************************/
621 : /* CPLExternalEntityLoader() */
622 : /************************************************************************/
623 :
624 : static const char szXML_XSD[] = "<schema xmlns=\"http://www.w3.org/2001/XMLSchema\" targetNamespace=\"http://www.w3.org/XML/1998/namespace\">"
625 : "<attribute name=\"lang\">"
626 : "<simpleType>"
627 : "<union memberTypes=\"language\">"
628 : "<simpleType>"
629 : "<restriction base=\"string\">"
630 : "<enumeration value=\"\"/>"
631 : "</restriction>"
632 : "</simpleType>"
633 : "</union>"
634 : "</simpleType>"
635 : "</attribute>"
636 : "<attribute name=\"space\">"
637 : "<simpleType>"
638 : "<restriction base=\"NCName\">"
639 : "<enumeration value=\"default\"/>"
640 : "<enumeration value=\"preserve\"/>"
641 : "</restriction>"
642 : "</simpleType>"
643 : "</attribute>"
644 : "<attribute name=\"base\" type=\"anyURI\"/>"
645 : "<attribute name=\"id\" type=\"ID\"/>"
646 : "<attributeGroup name=\"specialAttrs\">"
647 : "<attribute ref=\"xml:base\"/>"
648 : "<attribute ref=\"xml:lang\"/>"
649 : "<attribute ref=\"xml:space\"/>"
650 : "<attribute ref=\"xml:id\"/>"
651 : "</attributeGroup>"
652 : "</schema>";
653 :
654 : /* Simplified (and truncated) version of http://www.w3.org/1999/xlink.xsd (sufficient for GML schemas) */
655 : static const char szXLINK_XSD[] = "<schema xmlns=\"http://www.w3.org/2001/XMLSchema\" targetNamespace=\"http://www.w3.org/1999/xlink\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">"
656 : "<attribute name=\"type\" type=\"string\"/>"
657 : "<attribute name=\"href\" type=\"anyURI\"/>"
658 : "<attribute name=\"role\" type=\"anyURI\"/>"
659 : "<attribute name=\"arcrole\" type=\"anyURI\"/>"
660 : "<attribute name=\"title\" type=\"string\"/>"
661 : "<attribute name=\"show\" type=\"string\"/>"
662 : "<attribute name=\"actuate\" type=\"string\"/>"
663 : "<attribute name=\"label\" type=\"NCName\"/>"
664 : "<attribute name=\"from\" type=\"NCName\"/>"
665 : "<attribute name=\"to\" type=\"NCName\"/>"
666 : "<attributeGroup name=\"simpleAttrs\">"
667 : "<attribute ref=\"xlink:type\" fixed=\"simple\"/>"
668 : "<attribute ref=\"xlink:href\"/>"
669 : "<attribute ref=\"xlink:role\"/>"
670 : "<attribute ref=\"xlink:arcrole\"/>"
671 : "<attribute ref=\"xlink:title\"/>"
672 : "<attribute ref=\"xlink:show\"/>"
673 : "<attribute ref=\"xlink:actuate\"/>"
674 : "</attributeGroup>"
675 : "</schema>";
676 :
677 : static
678 210 : xmlParserInputPtr CPLExternalEntityLoader (const char * URL,
679 : const char * ID,
680 : xmlParserCtxtPtr context)
681 : {
682 : //CPLDebug("CPL", "CPLExternalEntityLoader(%s)", URL);
683 210 : CPLString osURL;
684 :
685 : /* Use libxml2 catalog mechanism to resolve the URL to something else */
686 210 : xmlChar* pszResolved = xmlCatalogResolveSystem((const xmlChar*)URL);
687 210 : if (pszResolved == NULL)
688 210 : pszResolved = xmlCatalogResolveURI((const xmlChar*)URL);
689 210 : if (pszResolved)
690 : {
691 0 : CPLDebug("CPL", "Resolving %s in %s", URL, (const char*)pszResolved );
692 0 : osURL = (const char*)pszResolved;
693 0 : URL = osURL.c_str();
694 0 : xmlFree(pszResolved);
695 0 : pszResolved = NULL;
696 : }
697 :
698 210 : if (strncmp(URL, "http://", 7) == 0)
699 : {
700 : /* Make sure to use http://schemas.opengis.net/ */
701 : /* when gml/2 or gml/3 is detected */
702 171 : const char* pszGML = strstr(URL, "gml/2");
703 171 : if (pszGML == NULL)
704 161 : pszGML = strstr(URL, "gml/3");
705 171 : if (pszGML != NULL)
706 : {
707 65 : osURL = "http://schemas.opengis.net/";
708 65 : osURL += pszGML;
709 65 : URL = osURL.c_str();
710 : }
711 106 : else if (strcmp(URL, "http://www.w3.org/2001/xml.xsd") == 0)
712 : {
713 1 : CPLString osTmp = CPLFindLocalXSD("xml.xsd");
714 1 : if( osTmp.size() != 0 )
715 : {
716 0 : osURL = osTmp;
717 0 : URL = osURL.c_str();
718 : }
719 : else
720 : {
721 1 : CPLDebug("CPL", "Resolving %s to local definition", "http://www.w3.org/2001/xml.xsd");
722 1 : return xmlNewStringInputStream(context, (const xmlChar*) szXML_XSD);
723 0 : }
724 : }
725 105 : else if (strcmp(URL, "http://www.w3.org/1999/xlink.xsd") == 0)
726 : {
727 0 : CPLString osTmp = CPLFindLocalXSD("xlink.xsd");
728 0 : if( osTmp.size() != 0 )
729 : {
730 0 : osURL = osTmp;
731 0 : URL = osURL.c_str();
732 : }
733 : else
734 : {
735 0 : CPLDebug("CPL", "Resolving %s to local definition", "http://www.w3.org/1999/xlink.xsd");
736 0 : return xmlNewStringInputStream(context, (const xmlChar*) szXLINK_XSD);
737 0 : }
738 : }
739 105 : else if (strncmp(URL, "http://schemas.opengis.net/",
740 : strlen("http://schemas.opengis.net/")) != 0)
741 : {
742 0 : CPLDebug("CPL", "Loading %s", URL);
743 0 : return pfnLibXMLOldExtranerEntityLoader(URL, ID, context);
744 : }
745 : }
746 39 : else if (strncmp(URL, "ftp://", 6) == 0)
747 : {
748 0 : return pfnLibXMLOldExtranerEntityLoader(URL, ID, context);
749 : }
750 39 : else if (strncmp(URL, "file://", 7) == 0)
751 : {
752 : /* Parse file:// URI so as to be able to open them with VSI*L API */
753 0 : if (strncmp(URL, "file://localhost/", 17) == 0)
754 0 : URL += 16;
755 : else
756 0 : URL += 7;
757 0 : if (URL[0] == '/' && URL[1] != '\0' && URL[2] == ':' && URL[3] == '/') /* Windows */
758 0 : URL ++;
759 0 : else if (URL[0] == '/') /* Unix */
760 : ;
761 : else
762 0 : return pfnLibXMLOldExtranerEntityLoader(URL, ID, context);
763 : }
764 :
765 209 : CPLString osModURL;
766 209 : if (strncmp(URL, "/vsizip/vsicurl/http%3A//",
767 : strlen("/vsizip/vsicurl/http%3A//")) == 0)
768 : {
769 0 : osModURL = "/vsizip/vsicurl/http://";
770 0 : osModURL += URL + strlen("/vsizip/vsicurl/http%3A//");
771 : }
772 209 : else if (strncmp(URL, "/vsicurl/http%3A//",
773 : strlen("/vsicurl/http%3A//")) == 0)
774 : {
775 0 : osModURL = "vsicurl/http://";
776 0 : osModURL += URL + strlen("/vsicurl/http%3A//");
777 : }
778 209 : else if (strncmp(URL, "http://schemas.opengis.net/",
779 : strlen("http://schemas.opengis.net/")) == 0)
780 : {
781 : const char *pszAfterOpenGIS =
782 170 : URL + strlen("http://schemas.opengis.net/");
783 :
784 : const char *pszSchemasOpenGIS;
785 :
786 170 : pszSchemasOpenGIS = CPLGetConfigOption("GDAL_OPENGIS_SCHEMAS", NULL);
787 170 : if (pszSchemasOpenGIS != NULL)
788 : {
789 170 : int nLen = (int)strlen(pszSchemasOpenGIS);
790 170 : if (nLen > 0 && pszSchemasOpenGIS[nLen-1] == '/')
791 : {
792 0 : osModURL = pszSchemasOpenGIS;
793 0 : osModURL += pszAfterOpenGIS;
794 : }
795 : else
796 : {
797 170 : osModURL = pszSchemasOpenGIS;
798 170 : osModURL += "/";
799 170 : osModURL += pszAfterOpenGIS;
800 : }
801 : }
802 0 : else if ((pszSchemasOpenGIS = CPLFindFile( "gdal", "SCHEMAS_OPENGIS_NET" )) != NULL)
803 : {
804 0 : osModURL = pszSchemasOpenGIS;
805 0 : osModURL += "/";
806 0 : osModURL += pszAfterOpenGIS;
807 : }
808 0 : else if ((pszSchemasOpenGIS = CPLFindFile( "gdal", "SCHEMAS_OPENGIS_NET.zip" )) != NULL)
809 : {
810 0 : osModURL = "/vsizip/";
811 0 : osModURL += pszSchemasOpenGIS;
812 0 : osModURL += "/";
813 0 : osModURL += pszAfterOpenGIS;
814 : }
815 : else
816 : {
817 0 : osModURL = "/vsizip/vsicurl/http://schemas.opengis.net/SCHEMAS_OPENGIS_NET.zip/";
818 0 : osModURL += pszAfterOpenGIS;
819 : }
820 : }
821 : else
822 : {
823 39 : osModURL = URL;
824 : }
825 :
826 209 : xmlChar* pszBuffer = (xmlChar*)CPLLoadSchemaStr(osModURL);
827 209 : if (pszBuffer == NULL)
828 0 : return NULL;
829 :
830 209 : xmlParserInputPtr poInputStream = xmlNewStringInputStream(context, pszBuffer);
831 209 : if (poInputStream != NULL)
832 209 : poInputStream->free = CPLLibXMLInputStreamCPLFree;
833 209 : return poInputStream;
834 : }
835 :
836 : /************************************************************************/
837 : /* CPLLibXMLWarningErrorCallback() */
838 : /************************************************************************/
839 :
840 22 : static void CPLLibXMLWarningErrorCallback (void * ctx, const char * msg, ...)
841 : {
842 : va_list varg;
843 : char * pszStr;
844 :
845 22 : va_start(varg, msg);
846 22 : pszStr = (char *)va_arg( varg, char *);
847 :
848 22 : if (strstr(pszStr, "since this namespace was already imported") == NULL)
849 : {
850 0 : xmlErrorPtr pErrorPtr = xmlGetLastError();
851 0 : const char* pszFilename = (const char*)ctx;
852 0 : char* pszStrDup = CPLStrdup(pszStr);
853 0 : int nLen = (int)strlen(pszStrDup);
854 0 : if (nLen > 0 && pszStrDup[nLen-1] == '\n')
855 0 : pszStrDup[nLen-1] = '\0';
856 : CPLError(CE_Failure, CPLE_AppDefined, "libXML: %s:%d: %s",
857 0 : pszFilename, pErrorPtr ? pErrorPtr->line : 0, pszStrDup);
858 0 : CPLFree(pszStrDup);
859 : }
860 :
861 22 : va_end(varg);
862 22 : }
863 :
864 : /************************************************************************/
865 : /* CPLLoadContentFromFile() */
866 : /************************************************************************/
867 :
868 : static
869 33 : char* CPLLoadContentFromFile(const char* pszFilename)
870 : {
871 33 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
872 33 : if (fp == NULL)
873 0 : return NULL;
874 : vsi_l_offset nSize;
875 33 : VSIFSeekL(fp, 0, SEEK_END);
876 33 : nSize = VSIFTellL(fp);
877 33 : VSIFSeekL(fp, 0, SEEK_SET);
878 33 : if ((vsi_l_offset)(int)nSize != nSize ||
879 : nSize > INT_MAX - 1 )
880 : {
881 0 : VSIFCloseL(fp);
882 0 : return NULL;
883 : }
884 33 : char* pszBuffer = (char*)VSIMalloc(nSize + 1);
885 33 : if (pszBuffer == NULL)
886 : {
887 0 : VSIFCloseL(fp);
888 0 : return NULL;
889 : }
890 33 : VSIFReadL(pszBuffer, 1, nSize, fp);
891 33 : pszBuffer[nSize] = '\0';
892 33 : VSIFCloseL(fp);
893 33 : return pszBuffer;
894 : }
895 :
896 : /************************************************************************/
897 : /* CPLLoadXMLSchema() */
898 : /************************************************************************/
899 :
900 : typedef void* CPLXMLSchemaPtr;
901 :
902 : /**
903 : * \brief Load a XSD schema.
904 : *
905 : * The return value should be freed with CPLFreeXMLSchema().
906 : *
907 : * @param pszXSDFilename XSD schema to load.
908 : * @return a handle to the parsed XML schema, or NULL in case of failure.
909 : *
910 : * @since GDAL 1.10.0
911 : */
912 :
913 : static
914 38 : CPLXMLSchemaPtr CPLLoadXMLSchema(const char* pszXSDFilename)
915 : {
916 38 : char* pszStr = CPLLoadSchemaStr(pszXSDFilename);
917 38 : if (pszStr == NULL)
918 0 : return NULL;
919 :
920 38 : xmlExternalEntityLoader pfnLibXMLOldExtranerEntityLoaderLocal = NULL;
921 38 : pfnLibXMLOldExtranerEntityLoaderLocal = xmlGetExternalEntityLoader();
922 38 : pfnLibXMLOldExtranerEntityLoader = pfnLibXMLOldExtranerEntityLoaderLocal;
923 38 : xmlSetExternalEntityLoader(CPLExternalEntityLoader);
924 :
925 : xmlSchemaParserCtxtPtr pSchemaParserCtxt =
926 38 : xmlSchemaNewMemParserCtxt(pszStr, strlen(pszStr));
927 :
928 : xmlSchemaSetParserErrors(pSchemaParserCtxt,
929 : CPLLibXMLWarningErrorCallback,
930 : CPLLibXMLWarningErrorCallback,
931 38 : NULL);
932 :
933 38 : xmlSchemaPtr pSchema = xmlSchemaParse(pSchemaParserCtxt);
934 38 : xmlSchemaFreeParserCtxt(pSchemaParserCtxt);
935 :
936 38 : xmlSetExternalEntityLoader(pfnLibXMLOldExtranerEntityLoaderLocal);
937 :
938 38 : CPLFree(pszStr);
939 :
940 38 : return (CPLXMLSchemaPtr) pSchema;
941 : }
942 :
943 : /************************************************************************/
944 : /* CPLFreeXMLSchema() */
945 : /************************************************************************/
946 :
947 : /**
948 : * \brief Free a XSD schema.
949 : *
950 : * @param pSchema a handle to the parsed XML schema.
951 : *
952 : * @since GDAL 1.10.0
953 : */
954 :
955 : static
956 38 : void CPLFreeXMLSchema(CPLXMLSchemaPtr pSchema)
957 : {
958 38 : if (pSchema)
959 38 : xmlSchemaFree((xmlSchemaPtr)pSchema);
960 38 : }
961 :
962 : /************************************************************************/
963 : /* CPLValidateXML() */
964 : /************************************************************************/
965 :
966 : /**
967 : * \brief Validate a XML file against a XML schema.
968 : *
969 : * @param pszXMLFilename the filename of the XML file to validate.
970 : * @param pszXSDFilename the filename of the XSD schema.
971 : * @param papszOptions unused for now.
972 : * @return TRUE if the XML file validates against the XML schema.
973 : *
974 : * @since GDAL 1.10.0
975 : */
976 :
977 38 : int CPLValidateXML(const char* pszXMLFilename,
978 : const char* pszXSDFilename,
979 : char** papszOptions)
980 : {
981 38 : VSILFILE* fpXML = VSIFOpenL(pszXMLFilename, "rb");
982 38 : if (fpXML == NULL)
983 : {
984 : CPLError(CE_Failure, CPLE_OpenFailed,
985 0 : "Cannot open %s", pszXMLFilename);
986 0 : return FALSE;
987 : }
988 38 : CPLString osTmpXSDFilename;
989 : char szHeader[2048];
990 38 : int nRead = (int)VSIFReadL(szHeader, 1, sizeof(szHeader)-1, fpXML);
991 38 : szHeader[nRead] = '\0';
992 38 : VSIFCloseL(fpXML);
993 :
994 : /* Workaround following bug : "element FeatureCollection: Schemas validity error : Element '{http://www.opengis.net/wfs}FeatureCollection': No matching global declaration available for the validation root" */
995 : /* We create a wrapping XSD that imports the WFS .xsd (and possibly the GML .xsd too) and the application schema */
996 : /* This is a known libxml2 limitation */
997 38 : if (strstr(szHeader, "<wfs:FeatureCollection") ||
998 : (strstr(szHeader, "<FeatureCollection") && strstr(szHeader, "xmlns:wfs=\"http://www.opengis.net/wfs\"")))
999 : {
1000 3 : const char* pszWFSSchemaNamespace = "http://www.opengis.net/wfs";
1001 3 : const char* pszWFSSchemaLocation = NULL;
1002 3 : const char* pszGMLSchemaLocation = NULL;
1003 3 : if (strstr(szHeader, "wfs/1.0.0/WFS-basic.xsd"))
1004 : {
1005 1 : pszWFSSchemaLocation = "http://schemas.opengis.net/wfs/1.0.0/WFS-basic.xsd";
1006 : }
1007 2 : else if (strstr(szHeader, "wfs/1.1.0/wfs.xsd"))
1008 : {
1009 1 : pszWFSSchemaLocation = "http://schemas.opengis.net/wfs/1.1.0/wfs.xsd";
1010 : }
1011 1 : else if (strstr(szHeader, "wfs/2.0/wfs.xsd"))
1012 : {
1013 1 : pszWFSSchemaNamespace = "http://www.opengis.net/wfs/2.0";
1014 1 : pszWFSSchemaLocation = "http://schemas.opengis.net/wfs/2.0/wfs.xsd";
1015 : }
1016 :
1017 3 : VSILFILE* fpXSD = VSIFOpenL(pszXSDFilename, "rb");
1018 3 : if (fpXSD == NULL)
1019 : {
1020 : CPLError(CE_Failure, CPLE_OpenFailed,
1021 0 : "Cannot open %s", pszXSDFilename);
1022 0 : return FALSE;
1023 : }
1024 3 : int nRead = (int)VSIFReadL(szHeader, 1, sizeof(szHeader)-1, fpXSD);
1025 3 : szHeader[nRead] = '\0';
1026 3 : VSIFCloseL(fpXSD);
1027 :
1028 3 : if (strstr(szHeader, "gml/3.1.1") != NULL &&
1029 : strstr(szHeader, "gml/3.1.1/base/gml.xsd") == NULL)
1030 : {
1031 1 : pszGMLSchemaLocation = "http://schemas.opengis.net/gml/3.1.1/base/gml.xsd";
1032 : }
1033 :
1034 3 : if (pszWFSSchemaLocation != NULL)
1035 : {
1036 3 : osTmpXSDFilename = CPLSPrintf("/vsimem/CPLValidateXML_%p_%p.xsd", pszXMLFilename, pszXSDFilename);
1037 3 : char* pszEscapedXSDFilename = CPLEscapeString(pszXSDFilename, -1, CPLES_XML);
1038 3 : VSILFILE* fpMEM = VSIFOpenL(osTmpXSDFilename, "wb");
1039 3 : VSIFPrintfL(fpMEM, "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n");
1040 3 : VSIFPrintfL(fpMEM, " <xs:import namespace=\"%s\" schemaLocation=\"%s\"/>\n", pszWFSSchemaNamespace, pszWFSSchemaLocation);
1041 3 : VSIFPrintfL(fpMEM, " <xs:import namespace=\"ignored\" schemaLocation=\"%s\"/>\n", pszEscapedXSDFilename);
1042 3 : if (pszGMLSchemaLocation)
1043 1 : VSIFPrintfL(fpMEM, " <xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"%s\"/>\n", pszGMLSchemaLocation);
1044 3 : VSIFPrintfL(fpMEM, "</xs:schema>\n");
1045 3 : VSIFCloseL(fpMEM);
1046 3 : CPLFree(pszEscapedXSDFilename);
1047 : }
1048 : }
1049 :
1050 38 : CPLXMLSchemaPtr pSchema = CPLLoadXMLSchema(osTmpXSDFilename.size() ? osTmpXSDFilename.c_str() : pszXSDFilename);
1051 38 : if (osTmpXSDFilename.size())
1052 3 : VSIUnlink(osTmpXSDFilename);
1053 38 : if (pSchema == NULL)
1054 0 : return FALSE;
1055 :
1056 : xmlSchemaValidCtxtPtr pSchemaValidCtxt;
1057 :
1058 38 : pSchemaValidCtxt = xmlSchemaNewValidCtxt((xmlSchemaPtr)pSchema);
1059 :
1060 38 : if (pSchemaValidCtxt == NULL)
1061 : {
1062 0 : CPLFreeXMLSchema(pSchema);
1063 0 : return FALSE;
1064 : }
1065 :
1066 : xmlSchemaSetValidErrors(pSchemaValidCtxt,
1067 : CPLLibXMLWarningErrorCallback,
1068 : CPLLibXMLWarningErrorCallback,
1069 38 : (void*) pszXMLFilename);
1070 :
1071 38 : int bValid = FALSE;
1072 38 : if (strncmp(pszXMLFilename, "/vsi", 4) != 0)
1073 : {
1074 : bValid =
1075 5 : xmlSchemaValidateFile(pSchemaValidCtxt, pszXMLFilename, 0) == 0;
1076 : }
1077 : else
1078 : {
1079 33 : char* pszXML = CPLLoadContentFromFile(pszXMLFilename);
1080 33 : if (pszXML != NULL)
1081 : {
1082 33 : xmlDocPtr pDoc = xmlParseDoc((const xmlChar *)pszXML);
1083 33 : if (pDoc != NULL)
1084 : {
1085 33 : bValid = xmlSchemaValidateDoc(pSchemaValidCtxt, pDoc) == 0;
1086 : }
1087 33 : xmlFreeDoc(pDoc);
1088 : }
1089 33 : CPLFree(pszXML);
1090 : }
1091 38 : xmlSchemaFreeValidCtxt(pSchemaValidCtxt);
1092 38 : CPLFreeXMLSchema(pSchema);
1093 :
1094 38 : return bValid;
1095 : }
1096 :
1097 : #else // HAVE_RECENT_LIBXML2
1098 :
1099 : /************************************************************************/
1100 : /* CPLValidateXML() */
1101 : /************************************************************************/
1102 :
1103 : int CPLValidateXML(const char* pszXMLFilename,
1104 : const char* pszXSDFilename,
1105 : char** papszOptions)
1106 : {
1107 : CPLError(CE_Failure, CPLE_NotSupported,
1108 : "%s not implemented due to missing libxml2 support",
1109 : "CPLValidateXML()");
1110 : return FALSE;
1111 : }
1112 :
1113 : #endif // HAVE_RECENT_LIBXML2
|