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 50 : static void CPLFixPath(char* pszPath)
74 : {
75 3803 : for(int i=0;pszPath[i] != '\0';i++)
76 : {
77 3753 : if (pszPath[i] == '\\')
78 0 : pszPath[i] = '/';
79 : }
80 :
81 0 : while(TRUE)
82 : {
83 50 : char* pszSlashDotDot = strstr(pszPath, "/../");
84 50 : if (pszSlashDotDot == NULL || pszSlashDotDot == pszPath)
85 50 : return;
86 0 : char* pszSlashBefore = pszSlashDotDot-1;
87 0 : while(pszSlashBefore > pszPath && *pszSlashBefore != '/')
88 0 : pszSlashBefore --;
89 0 : if (pszSlashBefore == pszPath)
90 0 : return;
91 : memmove(pszSlashBefore + 1, pszSlashDotDot + 4,
92 0 : 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 : static void CPLHasLibXMLBugWarningCallback (void * ctx, const char * msg, ...)
105 : {
106 : }
107 :
108 : /************************************************************************/
109 : /* CPLHasLibXMLBug() */
110 : /************************************************************************/
111 :
112 : static int CPLHasLibXMLBug()
113 : {
114 : if (bHasLibXMLBug >= 0)
115 : 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 : pSchemaParserCtxt = xmlSchemaNewMemParserCtxt(szLibXMLBugTester, strlen(szLibXMLBugTester));
140 :
141 : xmlSchemaSetParserErrors(pSchemaParserCtxt,
142 : CPLHasLibXMLBugWarningCallback,
143 : CPLHasLibXMLBugWarningCallback,
144 : NULL);
145 :
146 : pSchema = xmlSchemaParse(pSchemaParserCtxt);
147 : xmlSchemaFreeParserCtxt(pSchemaParserCtxt);
148 :
149 : bHasLibXMLBug = (pSchema == NULL);
150 :
151 : if (pSchema)
152 : xmlSchemaFree(pSchema);
153 :
154 : if (bHasLibXMLBug)
155 : {
156 : CPLDebug("CPL",
157 : "LibXML bug found (cf https://bugzilla.gnome.org/show_bug.cgi?id=630130). "
158 : "Will try to workaround for GML schemas.");
159 : }
160 :
161 : return bHasLibXMLBug;
162 : }
163 :
164 : #endif
165 :
166 : /************************************************************************/
167 : /* CPLExtractSubSchema() */
168 : /************************************************************************/
169 :
170 28 : static CPLXMLNode* CPLExtractSubSchema(CPLXMLNode* psSubXML, CPLXMLNode* psMainSchema)
171 : {
172 28 : if (psSubXML->eType == CXT_Element && strcmp(psSubXML->pszValue, "?xml") == 0)
173 : {
174 28 : CPLXMLNode* psNext = psSubXML->psNext;
175 28 : psSubXML->psNext = NULL;
176 28 : CPLDestroyXMLNode(psSubXML);
177 28 : psSubXML = psNext;
178 : }
179 :
180 28 : if (psSubXML != NULL && psSubXML->eType == CXT_Comment)
181 : {
182 3 : CPLXMLNode* psNext = psSubXML->psNext;
183 3 : psSubXML->psNext = NULL;
184 3 : CPLDestroyXMLNode(psSubXML);
185 3 : psSubXML = psNext;
186 : }
187 :
188 28 : 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 28 : CPLXMLNode* psNext = psSubXML->psChild;
195 224 : 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 168 : if (psNext->eType == CXT_Attribute &&
200 : strncmp(psNext->pszValue, "xmlns:", 6) == 0 &&
201 : CPLGetXMLValue(psMainSchema, psNext->pszValue, NULL) == NULL)
202 : {
203 21 : CPLXMLNode* psAttr = CPLCreateXMLNode(NULL, CXT_Attribute, psNext->pszValue);
204 21 : CPLCreateXMLNode(psAttr, CXT_Text, psNext->psChild->pszValue);
205 :
206 21 : psAttr->psNext = psMainSchema->psChild;
207 21 : psMainSchema->psChild = psAttr;
208 : }
209 168 : psNext = psNext->psNext;
210 : }
211 :
212 28 : if (psNext != NULL && psNext->eType != CXT_Element &&
213 : psNext->psNext != NULL && psNext->psNext->eType == CXT_Element)
214 : {
215 28 : CPLXMLNode* psNext2 = psNext->psNext;
216 28 : psNext->psNext = NULL;
217 28 : CPLDestroyXMLNode(psSubXML);
218 28 : psSubXML = psNext2;
219 : }
220 : }
221 :
222 28 : 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 : static int CPLWorkaroundLibXMLBug(CPLXMLNode* psIter)
232 : {
233 : 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 : CPLXMLNode* psIter2 = psIter->psChild;
239 : while(psIter2)
240 : {
241 : if (psIter2->eType == CXT_Attribute && strcmp(psIter2->pszValue, "type") == 0)
242 : {
243 : CPLFree(psIter2->psChild->pszValue);
244 : if (strcmp(CPLGetXMLValue(psIter, "substitutionGroup", ""), "gml:AbstractValue") == 0)
245 : psIter2->psChild->pszValue = CPLStrdup("gml:MeasureOrNilReasonListType"); /* GML 3.2.1 */
246 : else
247 : psIter2->psChild->pszValue = CPLStrdup("gml:MeasureOrNullListType");
248 : }
249 : psIter2 = psIter2->psNext;
250 : }
251 : }
252 :
253 : 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 : CPLXMLNode* psIter2 = psIter->psChild;
259 : while(psIter2)
260 : {
261 : if (psIter2->eType == CXT_Attribute && strcmp(psIter2->pszValue, "type") == 0)
262 : {
263 : CPLFree(psIter2->psChild->pszValue);
264 : if (strcmp(CPLGetXMLValue(psIter, "substitutionGroup", ""), "gml:AbstractValue") == 0)
265 : psIter2->psChild->pszValue = CPLStrdup("gml:CodeOrNilReasonListType"); /* GML 3.2.1 */
266 : else
267 : psIter2->psChild->pszValue = CPLStrdup("gml:CodeOrNullListType");
268 : }
269 : psIter2 = psIter2->psNext;
270 : }
271 : }
272 :
273 : 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 : return TRUE;
280 : }
281 :
282 : /* For GML 3.2.1 */
283 : else if (psIter->eType == CXT_Element &&
284 : strcmp(psIter->pszValue, "complexType") == 0 &&
285 : strcmp(CPLGetXMLValue(psIter, "name", ""), "VectorType") == 0)
286 : {
287 : CPLXMLNode* psSimpleContent = CPLCreateXMLNode(NULL, CXT_Element, "simpleContent");
288 : CPLXMLNode* psExtension = CPLCreateXMLNode(psSimpleContent, CXT_Element, "extension");
289 : CPLXMLNode* psExtensionBase = CPLCreateXMLNode(psExtension, CXT_Attribute, "base");
290 : CPLCreateXMLNode(psExtensionBase, CXT_Text, "gml:doubleList");
291 : CPLXMLNode* psAttributeGroup = CPLCreateXMLNode(psExtension, CXT_Element, "attributeGroup");
292 : CPLXMLNode* psAttributeGroupRef = CPLCreateXMLNode(psAttributeGroup, CXT_Attribute, "ref");
293 : CPLCreateXMLNode(psAttributeGroupRef, CXT_Text, "gml:SRSReferenceGroup");
294 :
295 : CPLXMLNode* psName = CPLCreateXMLNode(NULL, CXT_Attribute, "name");
296 : CPLCreateXMLNode(psName, CXT_Text, "VectorType");
297 :
298 : CPLDestroyXMLNode(psIter->psChild);
299 : psIter->psChild = psName;
300 : psIter->psChild->psNext = psSimpleContent;
301 : }
302 :
303 : 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 : CPLXMLNode* psComplexType = CPLCreateXMLNode(NULL, CXT_Element, "complexType");
310 : CPLXMLNode* psSequence = CPLCreateXMLNode(psComplexType, CXT_Element, "sequence");
311 : CPLXMLNode* psSequenceMinOccurs = CPLCreateXMLNode(psSequence, CXT_Attribute, "minOccurs");
312 : CPLCreateXMLNode(psSequenceMinOccurs, CXT_Text, "0");
313 : CPLXMLNode* psAny = CPLCreateXMLNode(psSequence, CXT_Element, "any");
314 : CPLXMLNode* psAnyMinOccurs = CPLCreateXMLNode(psAny, CXT_Attribute, "minOccurs");
315 : CPLCreateXMLNode(psAnyMinOccurs, CXT_Text, "0");
316 : CPLXMLNode* psAnyProcessContents = CPLCreateXMLNode(psAny, CXT_Attribute, " processContents");
317 : CPLCreateXMLNode(psAnyProcessContents, CXT_Text, "lax");
318 :
319 : CPLXMLNode* psName = CPLCreateXMLNode(NULL, CXT_Attribute, "name");
320 : CPLCreateXMLNode(psName, CXT_Text, CPLGetXMLValue(psIter, "name", ""));
321 :
322 : CPLDestroyXMLNode(psIter->psChild);
323 : psIter->psChild = psName;
324 : psIter->psChild->psNext = psComplexType;
325 : }
326 :
327 : return FALSE;
328 : }
329 : #endif
330 :
331 : /************************************************************************/
332 : /* CPLLoadSchemaStrInternal() */
333 : /************************************************************************/
334 :
335 : static
336 35 : CPLXMLNode* CPLLoadSchemaStrInternal(CPLHashSet* hSetSchemas,
337 : const char* pszFile)
338 : {
339 : CPLXMLNode* psXML;
340 : CPLXMLNode* psSchema;
341 : CPLXMLNode* psPrev;
342 : CPLXMLNode* psIter;
343 :
344 35 : if (CPLHashSetLookup(hSetSchemas, pszFile))
345 0 : return NULL;
346 :
347 35 : CPLHashSetInsert(hSetSchemas, CPLStrdup(pszFile));
348 :
349 35 : CPLDebug("CPL", "Parsing %s", pszFile);
350 :
351 35 : psXML = CPLParseXMLFile(pszFile);
352 35 : if (psXML == NULL)
353 : {
354 : CPLError(CE_Failure, CPLE_AppDefined,
355 0 : "Cannot open %s", pszFile);
356 0 : return NULL;
357 : }
358 :
359 35 : psSchema = CPLGetXMLNode(psXML, "=schema");
360 35 : if (psSchema == NULL)
361 1 : psSchema = CPLGetXMLNode(psXML, "=xs:schema");
362 35 : if (psSchema == NULL)
363 0 : psSchema = CPLGetXMLNode(psXML, "=xsd:schema");
364 35 : 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 35 : psPrev = NULL;
373 35 : psIter = psSchema->psChild;
374 2480 : while(psIter)
375 : {
376 2410 : int bDestroyCurrentNode = FALSE;
377 :
378 : #ifdef HAS_VALIDATION_BUG
379 : if (bHasLibXMLBug)
380 : bDestroyCurrentNode = CPLWorkaroundLibXMLBug(psIter);
381 : #endif
382 :
383 : /* Load the referenced schemas, and integrate them in the main schema */
384 2429 : 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 47 : const char* pszIncludeSchema = psIter->psChild->psChild->pszValue;
393 : char* pszFullFilename = CPLStrdup(
394 47 : CPLFormFilename(CPLGetPath(pszFile), pszIncludeSchema, NULL));
395 :
396 47 : CPLFixPath(pszFullFilename);
397 :
398 47 : CPLXMLNode* psSubXML = NULL;
399 :
400 : /* If we haven't yet loaded that schema, do it now */
401 47 : if (!CPLHashSetLookup(hSetSchemas, pszFullFilename))
402 : {
403 28 : psSubXML = CPLLoadSchemaStrInternal(hSetSchemas, pszFullFilename);
404 28 : if (psSubXML == NULL)
405 : {
406 0 : CPLFree(pszFullFilename);
407 0 : CPLDestroyXMLNode(psXML);
408 0 : return NULL;
409 : }
410 : }
411 47 : CPLFree(pszFullFilename);
412 47 : pszFullFilename = NULL;
413 :
414 47 : if (psSubXML)
415 : {
416 28 : CPLXMLNode* psNext = psIter->psNext;
417 :
418 28 : psSubXML = CPLExtractSubSchema(psSubXML, psSchema);
419 28 : if (psSubXML == NULL)
420 : {
421 0 : CPLDestroyXMLNode(psXML);
422 0 : return NULL;
423 : }
424 :
425 : /* Replace <include/> node by the subXML */
426 28 : CPLXMLNode* psIter2 = psSubXML;
427 5758 : while(psIter2->psNext)
428 5702 : psIter2 = psIter2->psNext;
429 28 : psIter2->psNext = psNext;
430 :
431 28 : if (psPrev == NULL)
432 0 : psSchema->psChild = psSubXML;
433 : else
434 28 : psPrev->psNext = psSubXML;
435 :
436 28 : psIter->psNext = NULL;
437 28 : CPLDestroyXMLNode(psIter);
438 :
439 28 : psPrev = psIter2;
440 28 : psIter = psNext;
441 28 : continue;
442 : }
443 : else
444 : {
445 : /* We have already included that file, */
446 : /* so just remove the <include/> node */
447 19 : bDestroyCurrentNode = TRUE;
448 : }
449 : }
450 :
451 : /* Patch the schemaLocation of <import/> */
452 2363 : 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 7 : CPLXMLNode* psIter2 = psIter->psChild;
458 28 : while(psIter2)
459 : {
460 14 : 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 3 : CPLGetPath(pszFile), psIter2->psChild->pszValue, NULL));
470 3 : CPLFixPath(pszFullFilename);
471 3 : CPLFree(psIter2->psChild->pszValue);
472 3 : psIter2->psChild->pszValue = pszFullFilename;
473 : }
474 14 : psIter2 = psIter2->psNext;
475 : }
476 : }
477 :
478 2382 : if (bDestroyCurrentNode)
479 : {
480 19 : CPLXMLNode* psNext = psIter->psNext;
481 19 : if (psPrev == NULL)
482 0 : psSchema->psChild = psNext;
483 : else
484 19 : psPrev->psNext = psNext;
485 :
486 19 : psIter->psNext = NULL;
487 19 : CPLDestroyXMLNode(psIter);
488 :
489 19 : psIter = psNext;
490 19 : continue;
491 : }
492 :
493 2363 : psPrev = psIter;
494 2363 : psIter = psIter->psNext;
495 : }
496 :
497 35 : return psXML;
498 : }
499 :
500 : /************************************************************************/
501 : /* CPLMoveImportAtBeginning() */
502 : /************************************************************************/
503 :
504 : static
505 7 : void CPLMoveImportAtBeginning(CPLXMLNode* psXML)
506 : {
507 : CPLXMLNode* psIter;
508 : CPLXMLNode* psPrev;
509 : CPLXMLNode* psSchema;
510 :
511 7 : psSchema = CPLGetXMLNode(psXML, "=schema");
512 7 : if (psSchema == NULL)
513 1 : psSchema = CPLGetXMLNode(psXML, "=xs:schema");
514 7 : if (psSchema == NULL)
515 0 : psSchema = CPLGetXMLNode(psXML, "=xsd:schema");
516 7 : if (psSchema == NULL)
517 0 : return;
518 :
519 7 : psPrev = NULL;
520 7 : psIter = psSchema->psChild;
521 2202 : while(psIter)
522 : {
523 2188 : 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 7 : CPLXMLNode* psNext = psIter->psNext;
530 :
531 7 : psPrev->psNext = psNext;
532 :
533 7 : CPLXMLNode* psFirstChild = psSchema->psChild;
534 7 : psSchema->psChild = psIter;
535 7 : psIter->psNext = psFirstChild;
536 :
537 7 : psIter = psNext;
538 7 : continue;
539 : }
540 :
541 2181 : psPrev = psIter;
542 2181 : psIter = psIter->psNext;
543 : }
544 : }
545 :
546 : /************************************************************************/
547 : /* CPLLoadSchemaStr() */
548 : /************************************************************************/
549 :
550 : static
551 7 : char* CPLLoadSchemaStr(const char* pszXSDFilename)
552 : {
553 7 : char* pszStr = NULL;
554 :
555 : #ifdef HAS_VALIDATION_BUG
556 : CPLHasLibXMLBug();
557 : #endif
558 :
559 : CPLHashSet* hSetSchemas =
560 7 : CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
561 : CPLXMLNode* psSchema =
562 7 : CPLLoadSchemaStrInternal(hSetSchemas, pszXSDFilename);
563 7 : if (psSchema)
564 : {
565 7 : CPLMoveImportAtBeginning(psSchema);
566 7 : pszStr = CPLSerializeXMLTree(psSchema);
567 7 : CPLDestroyXMLNode(psSchema);
568 : }
569 7 : CPLHashSetDestroy(hSetSchemas);
570 7 : return pszStr;
571 : }
572 :
573 : /************************************************************************/
574 : /* CPLLibXMLInputStreamCPLFree() */
575 : /************************************************************************/
576 :
577 6 : static void CPLLibXMLInputStreamCPLFree(xmlChar* pszBuffer)
578 : {
579 6 : CPLFree(pszBuffer);
580 6 : }
581 :
582 : /************************************************************************/
583 : /* CPLFindLocalXSD() */
584 : /************************************************************************/
585 :
586 0 : static CPLString CPLFindLocalXSD(const char* pszXSDFilename)
587 : {
588 : const char *pszSchemasOpenGIS;
589 0 : CPLString osTmp;
590 :
591 0 : pszSchemasOpenGIS = CPLGetConfigOption("GDAL_OPENGIS_SCHEMAS", NULL);
592 0 : if (pszSchemasOpenGIS != NULL)
593 : {
594 0 : int nLen = (int)strlen(pszSchemasOpenGIS);
595 0 : if (nLen > 0 && pszSchemasOpenGIS[nLen-1] == '/')
596 : {
597 0 : osTmp = pszSchemasOpenGIS;
598 0 : osTmp += pszXSDFilename;
599 : }
600 : else
601 : {
602 0 : osTmp = pszSchemasOpenGIS;
603 0 : osTmp += "/";
604 0 : 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 0 : if( VSIStatExL(osTmp, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 )
616 0 : return osTmp;
617 0 : 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 6 : xmlParserInputPtr CPLExternalEntityLoader (const char * URL,
679 : const char * ID,
680 : xmlParserCtxtPtr context)
681 : {
682 : //CPLDebug("CPL", "CPLExternalEntityLoader(%s)", URL);
683 6 : CPLString osURL;
684 :
685 : /* Use libxml2 catalog mechanism to resolve the URL to something else */
686 6 : xmlChar* pszResolved = xmlCatalogResolveSystem((const xmlChar*)URL);
687 6 : if (pszResolved == NULL)
688 6 : pszResolved = xmlCatalogResolveURI((const xmlChar*)URL);
689 6 : 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 6 : 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 4 : const char* pszGML = strstr(URL, "gml/2");
703 4 : if (pszGML == NULL)
704 4 : pszGML = strstr(URL, "gml/3");
705 4 : if (pszGML != NULL)
706 : {
707 3 : osURL = "http://schemas.opengis.net/";
708 3 : osURL += pszGML;
709 3 : URL = osURL.c_str();
710 : }
711 1 : else if (strcmp(URL, "http://www.w3.org/2001/xml.xsd") == 0)
712 : {
713 0 : CPLString osTmp = CPLFindLocalXSD("xml.xsd");
714 0 : if( osTmp.size() != 0 )
715 : {
716 0 : osURL = osTmp;
717 0 : URL = osURL.c_str();
718 : }
719 : else
720 : {
721 0 : CPLDebug("CPL", "Resolving %s to local definition", "http://www.w3.org/2001/xml.xsd");
722 0 : return xmlNewStringInputStream(context, (const xmlChar*) szXML_XSD);
723 0 : }
724 : }
725 1 : 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 1 : 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 2 : else if (strncmp(URL, "ftp://", 6) == 0)
747 : {
748 0 : return pfnLibXMLOldExtranerEntityLoader(URL, ID, context);
749 : }
750 2 : 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 6 : CPLString osModURL;
766 6 : 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 6 : 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 6 : else if (strncmp(URL, "http://schemas.opengis.net/",
779 : strlen("http://schemas.opengis.net/")) == 0)
780 : {
781 : const char *pszAfterOpenGIS =
782 4 : URL + strlen("http://schemas.opengis.net/");
783 :
784 : const char *pszSchemasOpenGIS;
785 :
786 4 : pszSchemasOpenGIS = CPLGetConfigOption("GDAL_OPENGIS_SCHEMAS", NULL);
787 4 : if (pszSchemasOpenGIS != NULL)
788 : {
789 4 : int nLen = (int)strlen(pszSchemasOpenGIS);
790 4 : if (nLen > 0 && pszSchemasOpenGIS[nLen-1] == '/')
791 : {
792 0 : osModURL = pszSchemasOpenGIS;
793 0 : osModURL += pszAfterOpenGIS;
794 : }
795 : else
796 : {
797 4 : osModURL = pszSchemasOpenGIS;
798 4 : osModURL += "/";
799 4 : 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 2 : osModURL = URL;
824 : }
825 :
826 6 : xmlChar* pszBuffer = (xmlChar*)CPLLoadSchemaStr(osModURL);
827 6 : if (pszBuffer == NULL)
828 0 : return NULL;
829 :
830 6 : xmlParserInputPtr poInputStream = xmlNewStringInputStream(context, pszBuffer);
831 6 : if (poInputStream != NULL)
832 6 : poInputStream->free = CPLLibXMLInputStreamCPLFree;
833 6 : return poInputStream;
834 : }
835 :
836 : /************************************************************************/
837 : /* CPLLibXMLWarningErrorCallback() */
838 : /************************************************************************/
839 :
840 3 : static void CPLLibXMLWarningErrorCallback (void * ctx, const char * msg, ...)
841 : {
842 : va_list varg;
843 : char * pszStr;
844 :
845 3 : va_start(varg, msg);
846 3 : pszStr = (char *)va_arg( varg, char *);
847 :
848 3 : if (strstr(pszStr, "since this namespace was already imported") == NULL)
849 : {
850 2 : xmlErrorPtr pErrorPtr = xmlGetLastError();
851 2 : const char* pszFilename = (const char*)ctx;
852 2 : char* pszStrDup = CPLStrdup(pszStr);
853 2 : int nLen = (int)strlen(pszStrDup);
854 2 : if (nLen > 0 && pszStrDup[nLen-1] == '\n')
855 2 : pszStrDup[nLen-1] = '\0';
856 : CPLError(CE_Failure, CPLE_AppDefined, "libXML: %s:%d: %s",
857 2 : pszFilename, pErrorPtr ? pErrorPtr->line : 0, pszStrDup);
858 2 : CPLFree(pszStrDup);
859 : }
860 :
861 3 : va_end(varg);
862 3 : }
863 :
864 : /************************************************************************/
865 : /* CPLLoadContentFromFile() */
866 : /************************************************************************/
867 :
868 : static
869 0 : char* CPLLoadContentFromFile(const char* pszFilename)
870 : {
871 0 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
872 0 : if (fp == NULL)
873 0 : return NULL;
874 : vsi_l_offset nSize;
875 0 : VSIFSeekL(fp, 0, SEEK_END);
876 0 : nSize = VSIFTellL(fp);
877 0 : VSIFSeekL(fp, 0, SEEK_SET);
878 0 : if ((vsi_l_offset)(int)nSize != nSize ||
879 : nSize > INT_MAX - 1 )
880 : {
881 0 : VSIFCloseL(fp);
882 0 : return NULL;
883 : }
884 0 : char* pszBuffer = (char*)VSIMalloc(nSize + 1);
885 0 : if (pszBuffer == NULL)
886 : {
887 0 : VSIFCloseL(fp);
888 0 : return NULL;
889 : }
890 0 : VSIFReadL(pszBuffer, 1, nSize, fp);
891 0 : pszBuffer[nSize] = '\0';
892 0 : VSIFCloseL(fp);
893 0 : 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 1 : CPLXMLSchemaPtr CPLLoadXMLSchema(const char* pszXSDFilename)
915 : {
916 1 : char* pszStr = CPLLoadSchemaStr(pszXSDFilename);
917 1 : if (pszStr == NULL)
918 0 : return NULL;
919 :
920 1 : xmlExternalEntityLoader pfnLibXMLOldExtranerEntityLoaderLocal = NULL;
921 1 : pfnLibXMLOldExtranerEntityLoaderLocal = xmlGetExternalEntityLoader();
922 1 : pfnLibXMLOldExtranerEntityLoader = pfnLibXMLOldExtranerEntityLoaderLocal;
923 1 : xmlSetExternalEntityLoader(CPLExternalEntityLoader);
924 :
925 : xmlSchemaParserCtxtPtr pSchemaParserCtxt =
926 1 : xmlSchemaNewMemParserCtxt(pszStr, strlen(pszStr));
927 :
928 : xmlSchemaSetParserErrors(pSchemaParserCtxt,
929 : CPLLibXMLWarningErrorCallback,
930 : CPLLibXMLWarningErrorCallback,
931 1 : NULL);
932 :
933 1 : xmlSchemaPtr pSchema = xmlSchemaParse(pSchemaParserCtxt);
934 1 : xmlSchemaFreeParserCtxt(pSchemaParserCtxt);
935 :
936 1 : xmlSetExternalEntityLoader(pfnLibXMLOldExtranerEntityLoaderLocal);
937 :
938 1 : CPLFree(pszStr);
939 :
940 1 : 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 0 : void CPLFreeXMLSchema(CPLXMLSchemaPtr pSchema)
957 : {
958 0 : if (pSchema)
959 0 : xmlSchemaFree((xmlSchemaPtr)pSchema);
960 0 : }
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 1 : int CPLValidateXML(const char* pszXMLFilename,
978 : const char* pszXSDFilename,
979 : char** papszOptions)
980 : {
981 1 : VSILFILE* fpXML = VSIFOpenL(pszXMLFilename, "rb");
982 1 : if (fpXML == NULL)
983 : {
984 : CPLError(CE_Failure, CPLE_OpenFailed,
985 0 : "Cannot open %s", pszXMLFilename);
986 0 : return FALSE;
987 : }
988 1 : CPLString osTmpXSDFilename;
989 : char szHeader[2048];
990 1 : int nRead = (int)VSIFReadL(szHeader, 1, sizeof(szHeader)-1, fpXML);
991 1 : szHeader[nRead] = '\0';
992 1 : 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 1 : if (strstr(szHeader, "<wfs:FeatureCollection") ||
998 : (strstr(szHeader, "<FeatureCollection") && strstr(szHeader, "xmlns:wfs=\"http://www.opengis.net/wfs\"")))
999 : {
1000 0 : const char* pszWFSSchemaNamespace = "http://www.opengis.net/wfs";
1001 0 : const char* pszWFSSchemaLocation = NULL;
1002 0 : const char* pszGMLSchemaLocation = NULL;
1003 0 : if (strstr(szHeader, "wfs/1.0.0/WFS-basic.xsd"))
1004 : {
1005 0 : pszWFSSchemaLocation = "http://schemas.opengis.net/wfs/1.0.0/WFS-basic.xsd";
1006 : }
1007 0 : else if (strstr(szHeader, "wfs/1.1.0/wfs.xsd"))
1008 : {
1009 0 : pszWFSSchemaLocation = "http://schemas.opengis.net/wfs/1.1.0/wfs.xsd";
1010 : }
1011 0 : else if (strstr(szHeader, "wfs/2.0/wfs.xsd"))
1012 : {
1013 0 : pszWFSSchemaNamespace = "http://www.opengis.net/wfs/2.0";
1014 0 : pszWFSSchemaLocation = "http://schemas.opengis.net/wfs/2.0/wfs.xsd";
1015 : }
1016 :
1017 0 : VSILFILE* fpXSD = VSIFOpenL(pszXSDFilename, "rb");
1018 0 : if (fpXSD == NULL)
1019 : {
1020 : CPLError(CE_Failure, CPLE_OpenFailed,
1021 0 : "Cannot open %s", pszXSDFilename);
1022 0 : return FALSE;
1023 : }
1024 0 : int nRead = (int)VSIFReadL(szHeader, 1, sizeof(szHeader)-1, fpXSD);
1025 0 : szHeader[nRead] = '\0';
1026 0 : VSIFCloseL(fpXSD);
1027 :
1028 0 : if (strstr(szHeader, "gml/3.1.1") != NULL &&
1029 : strstr(szHeader, "gml/3.1.1/base/gml.xsd") == NULL)
1030 : {
1031 0 : pszGMLSchemaLocation = "http://schemas.opengis.net/gml/3.1.1/base/gml.xsd";
1032 : }
1033 :
1034 0 : if (pszWFSSchemaLocation != NULL)
1035 : {
1036 0 : osTmpXSDFilename = CPLSPrintf("/vsimem/CPLValidateXML_%p_%p.xsd", pszXMLFilename, pszXSDFilename);
1037 0 : char* pszEscapedXSDFilename = CPLEscapeString(pszXSDFilename, -1, CPLES_XML);
1038 0 : VSILFILE* fpMEM = VSIFOpenL(osTmpXSDFilename, "wb");
1039 0 : VSIFPrintfL(fpMEM, "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n");
1040 0 : VSIFPrintfL(fpMEM, " <xs:import namespace=\"%s\" schemaLocation=\"%s\"/>\n", pszWFSSchemaNamespace, pszWFSSchemaLocation);
1041 0 : VSIFPrintfL(fpMEM, " <xs:import namespace=\"ignored\" schemaLocation=\"%s\"/>\n", pszEscapedXSDFilename);
1042 0 : if (pszGMLSchemaLocation)
1043 0 : VSIFPrintfL(fpMEM, " <xs:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"%s\"/>\n", pszGMLSchemaLocation);
1044 0 : VSIFPrintfL(fpMEM, "</xs:schema>\n");
1045 0 : VSIFCloseL(fpMEM);
1046 0 : CPLFree(pszEscapedXSDFilename);
1047 : }
1048 : }
1049 :
1050 1 : CPLXMLSchemaPtr pSchema = CPLLoadXMLSchema(osTmpXSDFilename.size() ? osTmpXSDFilename.c_str() : pszXSDFilename);
1051 1 : if (osTmpXSDFilename.size())
1052 0 : VSIUnlink(osTmpXSDFilename);
1053 1 : if (pSchema == NULL)
1054 1 : return FALSE;
1055 :
1056 : xmlSchemaValidCtxtPtr pSchemaValidCtxt;
1057 :
1058 0 : pSchemaValidCtxt = xmlSchemaNewValidCtxt((xmlSchemaPtr)pSchema);
1059 :
1060 0 : if (pSchemaValidCtxt == NULL)
1061 : {
1062 0 : CPLFreeXMLSchema(pSchema);
1063 0 : return FALSE;
1064 : }
1065 :
1066 : xmlSchemaSetValidErrors(pSchemaValidCtxt,
1067 : CPLLibXMLWarningErrorCallback,
1068 : CPLLibXMLWarningErrorCallback,
1069 0 : (void*) pszXMLFilename);
1070 :
1071 0 : int bValid = FALSE;
1072 0 : if (strncmp(pszXMLFilename, "/vsi", 4) != 0)
1073 : {
1074 : bValid =
1075 0 : xmlSchemaValidateFile(pSchemaValidCtxt, pszXMLFilename, 0) == 0;
1076 : }
1077 : else
1078 : {
1079 0 : char* pszXML = CPLLoadContentFromFile(pszXMLFilename);
1080 0 : if (pszXML != NULL)
1081 : {
1082 0 : xmlDocPtr pDoc = xmlParseDoc((const xmlChar *)pszXML);
1083 0 : if (pDoc != NULL)
1084 : {
1085 0 : bValid = xmlSchemaValidateDoc(pSchemaValidCtxt, pDoc) == 0;
1086 : }
1087 0 : xmlFreeDoc(pDoc);
1088 : }
1089 0 : CPLFree(pszXML);
1090 : }
1091 0 : xmlSchemaFreeValidCtxt(pSchemaValidCtxt);
1092 0 : CPLFreeXMLSchema(pSchema);
1093 :
1094 0 : 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
|