LCOV - code coverage report
Current view: directory - port - cpl_xml_validate.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 435 347 79.8 %
Date: 2012-12-26 Functions: 16 16 100.0 %

       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

Generated by: LCOV version 1.7