LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csw - ogrcswdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 497 526 94.5 %
Date: 2025-12-14 22:20:37 Functions: 31 31 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CSW Translator
       4             :  * Purpose:  Implements OGRCSWDriver.
       5             :  * Author:   Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogrsf_frmts.h"
      14             : #include "cpl_conv.h"
      15             : #include "cpl_http.h"
      16             : #include "ogr_p.h"
      17             : #include "ogr_swq.h"
      18             : #include "ogrwfsfilter.h"
      19             : #include "gmlutils.h"
      20             : #include "memdataset.h"
      21             : 
      22             : /************************************************************************/
      23             : /*                             OGRCSWLayer                              */
      24             : /************************************************************************/
      25             : 
      26             : class OGRCSWDataSource;
      27             : 
      28             : class OGRCSWLayer final : public OGRLayer
      29             : {
      30             :     OGRCSWDataSource *poDS;
      31             :     OGRFeatureDefn *poFeatureDefn;
      32             : 
      33             :     GDALDataset *poBaseDS;
      34             :     OGRLayer *poBaseLayer;
      35             : 
      36             :     int nPagingStartIndex;
      37             :     int nFeatureRead;
      38             :     int nFeaturesInCurrentPage;
      39             : 
      40             :     CPLString osQuery;
      41             :     CPLString osCSWWhere;
      42             : 
      43             :     std::string m_osTmpDir{};
      44             : 
      45             :     GDALDataset *FetchGetRecords();
      46             :     GIntBig GetFeatureCountWithHits();
      47             :     void BuildQuery();
      48             : 
      49             :   public:
      50             :     explicit OGRCSWLayer(OGRCSWDataSource *poDS);
      51             :     ~OGRCSWLayer() override;
      52             : 
      53             :     void ResetReading() override;
      54             :     OGRFeature *GetNextFeature() override;
      55             :     GIntBig GetFeatureCount(int bForce = FALSE) override;
      56             : 
      57           3 :     const OGRFeatureDefn *GetLayerDefn() const override
      58             :     {
      59           3 :         return poFeatureDefn;
      60             :     }
      61             : 
      62           1 :     int TestCapability(const char *) const override
      63             :     {
      64           1 :         return FALSE;
      65             :     }
      66             : 
      67             :     OGRErr ISetSpatialFilter(int iGeomField,
      68             :                              const OGRGeometry *poGeom) override;
      69             : 
      70             :     OGRErr SetAttributeFilter(const char *) override;
      71             : };
      72             : 
      73             : /************************************************************************/
      74             : /*                           OGRCSWDataSource                           */
      75             : /************************************************************************/
      76             : 
      77             : class OGRCSWDataSource final : public GDALDataset
      78             : {
      79             :     CPLString osBaseURL;
      80             :     CPLString osVersion;
      81             :     CPLString osElementSetName;
      82             :     CPLString osOutputSchema;
      83             :     int nMaxRecords;
      84             : 
      85             :     OGRCSWLayer *poLayer;
      86             :     bool bFullExtentRecordsAsNonSpatial;
      87             : 
      88             :     CPLHTTPResult *SendGetCapabilities();
      89             : 
      90             :   public:
      91             :     OGRCSWDataSource();
      92             :     ~OGRCSWDataSource() override;
      93             : 
      94             :     int Open(const char *pszFilename, char **papszOpenOptions);
      95             : 
      96           1 :     int GetLayerCount() const override
      97             :     {
      98           1 :         return poLayer != nullptr;
      99             :     }
     100             : 
     101             :     const OGRLayer *GetLayer(int) const override;
     102             : 
     103             :     static CPLHTTPResult *HTTPFetch(const char *pszURL, const char *pszPost);
     104             : 
     105          39 :     const CPLString &GetBaseURL()
     106             :     {
     107          39 :         return osBaseURL;
     108             :     }
     109             : 
     110          39 :     const CPLString &GetVersion()
     111             :     {
     112          39 :         return osVersion;
     113             :     }
     114             : 
     115          39 :     const CPLString &GetElementSetName()
     116             :     {
     117          39 :         return osElementSetName;
     118             :     }
     119             : 
     120          60 :     const CPLString &GetOutputSchema()
     121             :     {
     122          60 :         return osOutputSchema;
     123             :     }
     124             : 
     125          10 :     bool FullExtentRecordsAsNonSpatial()
     126             :     {
     127          10 :         return bFullExtentRecordsAsNonSpatial;
     128             :     }
     129             : 
     130          31 :     int GetMaxRecords()
     131             :     {
     132          31 :         return nMaxRecords;
     133             :     }
     134             : };
     135             : 
     136             : /************************************************************************/
     137             : /*                           OGRCSWLayer()                              */
     138             : /************************************************************************/
     139             : 
     140           4 : OGRCSWLayer::OGRCSWLayer(OGRCSWDataSource *poDSIn)
     141           4 :     : poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn("records")),
     142             :       poBaseDS(nullptr), poBaseLayer(nullptr), nPagingStartIndex(0),
     143           8 :       nFeatureRead(0), nFeaturesInCurrentPage(0)
     144             : {
     145           4 :     SetDescription(poFeatureDefn->GetName());
     146           4 :     poFeatureDefn->Reference();
     147           4 :     poFeatureDefn->SetGeomType(wkbPolygon);
     148             :     OGRSpatialReference *poSRS =
     149           4 :         new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
     150           4 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     151           4 :     poFeatureDefn->GetGeomFieldDefn(0)->SetName("boundingbox");
     152           4 :     poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     153             :     {
     154           8 :         OGRFieldDefn oField("identifier", OFTString);
     155           4 :         poFeatureDefn->AddFieldDefn(&oField);
     156             :     }
     157             :     {
     158           8 :         OGRFieldDefn oField("other_identifiers", OFTStringList);
     159           4 :         poFeatureDefn->AddFieldDefn(&oField);
     160             :     }
     161             :     {
     162           8 :         OGRFieldDefn oField("title", OFTString);
     163           4 :         poFeatureDefn->AddFieldDefn(&oField);
     164             :     }
     165             :     {
     166           8 :         OGRFieldDefn oField("type", OFTString);
     167           4 :         poFeatureDefn->AddFieldDefn(&oField);
     168             :     }
     169             :     {
     170           8 :         OGRFieldDefn oField("subject", OFTString);
     171           4 :         poFeatureDefn->AddFieldDefn(&oField);
     172             :     }
     173             :     {
     174           8 :         OGRFieldDefn oField("other_subjects", OFTStringList);
     175           4 :         poFeatureDefn->AddFieldDefn(&oField);
     176             :     }
     177             :     {
     178           8 :         OGRFieldDefn oField("references", OFTString);
     179           4 :         poFeatureDefn->AddFieldDefn(&oField);
     180             :     }
     181             :     {
     182           8 :         OGRFieldDefn oField("other_references", OFTStringList);
     183           4 :         poFeatureDefn->AddFieldDefn(&oField);
     184             :     }
     185             :     {
     186           8 :         OGRFieldDefn oField("modified", OFTString);
     187           4 :         poFeatureDefn->AddFieldDefn(&oField);
     188             :     }
     189             :     {
     190           8 :         OGRFieldDefn oField("abstract", OFTString);
     191           4 :         poFeatureDefn->AddFieldDefn(&oField);
     192             :     }
     193             :     {
     194           8 :         OGRFieldDefn oField("date", OFTString);
     195           4 :         poFeatureDefn->AddFieldDefn(&oField);
     196             :     }
     197             :     {
     198           8 :         OGRFieldDefn oField("language", OFTString);
     199           4 :         poFeatureDefn->AddFieldDefn(&oField);
     200             :     }
     201             :     {
     202           8 :         OGRFieldDefn oField("rights", OFTString);
     203           4 :         poFeatureDefn->AddFieldDefn(&oField);
     204             :     }
     205             :     {
     206           8 :         OGRFieldDefn oField("format", OFTString);
     207           4 :         poFeatureDefn->AddFieldDefn(&oField);
     208             :     }
     209             :     {
     210           8 :         OGRFieldDefn oField("other_formats", OFTStringList);
     211           4 :         poFeatureDefn->AddFieldDefn(&oField);
     212             :     }
     213             :     {
     214           8 :         OGRFieldDefn oField("creator", OFTString);
     215           4 :         poFeatureDefn->AddFieldDefn(&oField);
     216             :     }
     217             :     {
     218           8 :         OGRFieldDefn oField("source", OFTString);
     219           4 :         poFeatureDefn->AddFieldDefn(&oField);
     220             :     }
     221             :     {
     222           8 :         OGRFieldDefn oField("anytext", OFTString);
     223           4 :         poFeatureDefn->AddFieldDefn(&oField);
     224             :     }
     225           4 :     if (!poDS->GetOutputSchema().empty())
     226             :     {
     227           6 :         OGRFieldDefn oField("raw_xml", OFTString);
     228           3 :         poFeatureDefn->AddFieldDefn(&oField);
     229             :     }
     230             : 
     231           4 :     poSRS->Release();
     232             : 
     233           4 :     m_osTmpDir = VSIMemGenerateHiddenFilename("csw");
     234           4 : }
     235             : 
     236             : /************************************************************************/
     237             : /*                          ~OGRCSWLayer()                              */
     238             : /************************************************************************/
     239             : 
     240           8 : OGRCSWLayer::~OGRCSWLayer()
     241             : {
     242           4 :     poFeatureDefn->Release();
     243           4 :     GDALClose(poBaseDS);
     244           4 :     VSIRmdirRecursive(m_osTmpDir.c_str());
     245           8 : }
     246             : 
     247             : /************************************************************************/
     248             : /*                          ResetReading()                              */
     249             : /************************************************************************/
     250             : 
     251          28 : void OGRCSWLayer::ResetReading()
     252             : {
     253          28 :     nPagingStartIndex = 0;
     254          28 :     nFeatureRead = 0;
     255          28 :     nFeaturesInCurrentPage = 0;
     256          28 :     GDALClose(poBaseDS);
     257          28 :     poBaseDS = nullptr;
     258          28 :     poBaseLayer = nullptr;
     259          28 : }
     260             : 
     261             : /************************************************************************/
     262             : /*                          GetNextFeature()                            */
     263             : /************************************************************************/
     264             : 
     265          38 : OGRFeature *OGRCSWLayer::GetNextFeature()
     266             : {
     267             :     while (true)
     268             :     {
     269          38 :         if (nFeatureRead == nPagingStartIndex + nFeaturesInCurrentPage)
     270             :         {
     271          31 :             nPagingStartIndex = nFeatureRead;
     272             : 
     273          31 :             GDALClose(poBaseDS);
     274          31 :             poBaseLayer = nullptr;
     275             : 
     276          31 :             poBaseDS = FetchGetRecords();
     277          31 :             if (poBaseDS)
     278             :             {
     279          16 :                 poBaseLayer = poBaseDS->GetLayer(0);
     280          16 :                 poBaseLayer->ResetReading();
     281          16 :                 nFeaturesInCurrentPage = (int)poBaseLayer->GetFeatureCount();
     282             :             }
     283             :         }
     284          38 :         if (!poBaseLayer)
     285          15 :             return nullptr;
     286             : 
     287          23 :         OGRFeature *poSrcFeature = poBaseLayer->GetNextFeature();
     288          23 :         if (poSrcFeature == nullptr)
     289           0 :             return nullptr;
     290          23 :         nFeatureRead++;
     291             : 
     292          23 :         OGRFeature *poNewFeature = new OGRFeature(poFeatureDefn);
     293             : 
     294         440 :         for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     295             :         {
     296             :             const char *pszFieldname =
     297         417 :                 poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     298         417 :             int iSrcField = poSrcFeature->GetFieldIndex(pszFieldname);
     299             :             /* http://www.paikkatietohakemisto.fi/geonetwork/srv/en/csw returns
     300             :              * URI ... */
     301         417 :             if (iSrcField < 0 && strcmp(pszFieldname, "references") == 0)
     302           9 :                 iSrcField = poSrcFeature->GetFieldIndex("URI");
     303         417 :             if (iSrcField >= 0 && poSrcFeature->IsFieldSetAndNotNull(iSrcField))
     304             :             {
     305          73 :                 OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType();
     306             :                 OGRFieldType eSrcType =
     307          73 :                     poSrcFeature->GetFieldDefnRef(iSrcField)->GetType();
     308          73 :                 if (eType == eSrcType)
     309             :                 {
     310          31 :                     poNewFeature->SetField(
     311          31 :                         i, poSrcFeature->GetRawFieldRef(iSrcField));
     312             :                 }
     313             :                 else
     314             :                 {
     315          42 :                     if (eType == OFTString && eSrcType == OFTStringList &&
     316          28 :                         strcmp(pszFieldname, "identifier") == 0)
     317             :                     {
     318             :                         char **papszValues =
     319           7 :                             poSrcFeature->GetFieldAsStringList(iSrcField);
     320           7 :                         poNewFeature->SetField("identifier", *papszValues);
     321           7 :                         if (papszValues[1])
     322           7 :                             poNewFeature->SetField("other_identifiers",
     323           7 :                                                    papszValues + 1);
     324             :                     }
     325          35 :                     else if (eType == OFTString && eSrcType == OFTStringList &&
     326          21 :                              strcmp(pszFieldname, "subject") == 0)
     327             :                     {
     328             :                         char **papszValues =
     329           7 :                             poSrcFeature->GetFieldAsStringList(iSrcField);
     330           7 :                         poNewFeature->SetField("subject", *papszValues);
     331           7 :                         if (papszValues[1])
     332           7 :                             poNewFeature->SetField("other_subjects",
     333           7 :                                                    papszValues + 1);
     334             :                     }
     335          28 :                     else if (eType == OFTString && eSrcType == OFTStringList &&
     336          14 :                              strcmp(pszFieldname, "references") == 0)
     337             :                     {
     338             :                         char **papszValues =
     339           7 :                             poSrcFeature->GetFieldAsStringList(iSrcField);
     340           7 :                         poNewFeature->SetField("references", *papszValues);
     341           7 :                         if (papszValues[1])
     342           7 :                             poNewFeature->SetField("other_references",
     343           7 :                                                    papszValues + 1);
     344             :                     }
     345          21 :                     else if (eType == OFTString && eSrcType == OFTStringList &&
     346           7 :                              strcmp(pszFieldname, "format") == 0)
     347             :                     {
     348             :                         char **papszValues =
     349           7 :                             poSrcFeature->GetFieldAsStringList(iSrcField);
     350           7 :                         poNewFeature->SetField("format", *papszValues);
     351           7 :                         if (papszValues[1])
     352           7 :                             poNewFeature->SetField("other_formats",
     353           7 :                                                    papszValues + 1);
     354             :                     }
     355             :                     else
     356          14 :                         poNewFeature->SetField(
     357             :                             i, poSrcFeature->GetFieldAsString(iSrcField));
     358             :                 }
     359             :             }
     360             :         }
     361             : 
     362          23 :         OGRGeometry *poGeom = poSrcFeature->StealGeometry();
     363          23 :         if (poGeom)
     364             :         {
     365          10 :             if (poDS->FullExtentRecordsAsNonSpatial())
     366             :             {
     367           0 :                 OGREnvelope sEnvelope;
     368           0 :                 poGeom->getEnvelope(&sEnvelope);
     369           0 :                 if (sEnvelope.MinX == -180 && sEnvelope.MinY == -90 &&
     370           0 :                     sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90)
     371             :                 {
     372           0 :                     delete poGeom;
     373           0 :                     poGeom = nullptr;
     374             :                 }
     375             :             }
     376          10 :             if (poGeom)
     377             :             {
     378          10 :                 poGeom->assignSpatialReference(
     379          10 :                     poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
     380          10 :                 poNewFeature->SetGeometryDirectly(poGeom);
     381             :             }
     382             :         }
     383             : 
     384          23 :         poNewFeature->SetFID(nFeatureRead);
     385          23 :         delete poSrcFeature;
     386             : 
     387          23 :         if (osCSWWhere.empty() && m_poAttrQuery != nullptr &&
     388           0 :             !m_poAttrQuery->Evaluate(poNewFeature))
     389             :         {
     390           0 :             delete poNewFeature;
     391             :         }
     392             :         else
     393             :         {
     394          23 :             return poNewFeature;
     395             :         }
     396           0 :     }
     397             : }
     398             : 
     399             : /************************************************************************/
     400             : /*                         GetFeatureCount()                            */
     401             : /************************************************************************/
     402             : 
     403           8 : GIntBig OGRCSWLayer::GetFeatureCount(int bForce)
     404             : {
     405           8 :     GIntBig nFeatures = GetFeatureCountWithHits();
     406           8 :     if (nFeatures >= 0)
     407           2 :         return nFeatures;
     408           6 :     return OGRLayer::GetFeatureCount(bForce);
     409             : }
     410             : 
     411             : /************************************************************************/
     412             : /*                        GetFeatureCountWithHits()                     */
     413             : /************************************************************************/
     414             : 
     415           8 : GIntBig OGRCSWLayer::GetFeatureCountWithHits()
     416             : {
     417             :     CPLString osPost = CPLSPrintf(
     418             :         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
     419             :         "<csw:GetRecords resultType=\"hits\" service=\"CSW\" version=\"%s\""
     420             :         " xmlns:csw=\"http://www.opengis.net/cat/csw/2.0.2\""
     421             :         " xmlns:gml=\"http://www.opengis.net/gml\""
     422             :         " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
     423             :         " xmlns:dct=\"http://purl.org/dc/terms/\""
     424             :         " xmlns:ogc=\"http://www.opengis.net/ogc\""
     425             :         " xmlns:ows=\"http://www.opengis.net/ows\""
     426             :         " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
     427             :         " xsi:schemaLocation=\"http://www.opengis.net/cat/csw/2.0.2 "
     428             :         "http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\">"
     429             :         "<csw:Query typeNames=\"csw:Record\">"
     430             :         "<csw:ElementSetName>%s</csw:ElementSetName>"
     431             :         "%s"
     432             :         "</csw:Query>"
     433             :         "</csw:GetRecords>",
     434          16 :         poDS->GetVersion().c_str(), poDS->GetElementSetName().c_str(),
     435          32 :         osQuery.c_str());
     436             : 
     437             :     CPLHTTPResult *psResult =
     438           8 :         OGRCSWDataSource::HTTPFetch(poDS->GetBaseURL(), osPost);
     439           8 :     if (psResult == nullptr)
     440             :     {
     441           3 :         return -1;
     442             :     }
     443             : 
     444           5 :     CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
     445           5 :     if (psXML == nullptr)
     446             :     {
     447           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
     448             :                  psResult->pabyData);
     449           1 :         CPLHTTPDestroyResult(psResult);
     450           1 :         return -1;
     451             :     }
     452           4 :     CPLStripXMLNamespace(psXML, nullptr, TRUE);
     453           4 :     CPLHTTPDestroyResult(psResult);
     454           4 :     psResult = nullptr;
     455             : 
     456           4 :     GIntBig nFeatures = CPLAtoGIntBig(CPLGetXMLValue(
     457             :         psXML, "=GetRecordsResponse.SearchResults.numberOfRecordsMatched",
     458             :         "-1"));
     459             : 
     460           4 :     CPLDestroyXMLNode(psXML);
     461           4 :     return nFeatures;
     462             : }
     463             : 
     464             : /************************************************************************/
     465             : /*                         FetchGetRecords()                            */
     466             : /************************************************************************/
     467             : 
     468          31 : GDALDataset *OGRCSWLayer::FetchGetRecords()
     469             : {
     470          31 :     CPLHTTPResult *psResult = nullptr;
     471             : 
     472          62 :     CPLString osOutputSchema = poDS->GetOutputSchema();
     473          31 :     if (!osOutputSchema.empty())
     474           5 :         osOutputSchema = " outputSchema=\"" + osOutputSchema + "\"";
     475             : 
     476             :     CPLString osPost = CPLSPrintf(
     477             :         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
     478             :         "<csw:GetRecords resultType=\"results\" service=\"CSW\" version=\"%s\""
     479             :         "%s"
     480             :         " startPosition=\"%d\""
     481             :         " maxRecords=\"%d\""
     482             :         " xmlns:csw=\"http://www.opengis.net/cat/csw/2.0.2\""
     483             :         " xmlns:gml=\"http://www.opengis.net/gml\""
     484             :         " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
     485             :         " xmlns:dct=\"http://purl.org/dc/terms/\""
     486             :         " xmlns:ogc=\"http://www.opengis.net/ogc\""
     487             :         " xmlns:ows=\"http://www.opengis.net/ows\""
     488             :         " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
     489             :         " xsi:schemaLocation=\"http://www.opengis.net/cat/csw/2.0.2 "
     490             :         "http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\">"
     491             :         "<csw:Query typeNames=\"csw:Record\">"
     492             :         "<csw:ElementSetName>%s</csw:ElementSetName>"
     493             :         "%s"
     494             :         "</csw:Query>"
     495             :         "</csw:GetRecords>",
     496          31 :         poDS->GetVersion().c_str(), osOutputSchema.c_str(),
     497          62 :         nPagingStartIndex + 1, poDS->GetMaxRecords(),
     498          93 :         poDS->GetElementSetName().c_str(), osQuery.c_str());
     499             : 
     500          31 :     psResult = OGRCSWDataSource::HTTPFetch(poDS->GetBaseURL(), osPost);
     501          31 :     if (psResult == nullptr)
     502             :     {
     503           5 :         return nullptr;
     504             :     }
     505             : 
     506          26 :     VSIMkdir(m_osTmpDir.c_str(), 0);
     507             : 
     508          26 :     GByte *pabyData = psResult->pabyData;
     509          26 :     int nDataLen = psResult->nDataLen;
     510             : 
     511          26 :     if (strstr((const char *)pabyData, "<ServiceExceptionReport") != nullptr ||
     512          25 :         strstr((const char *)pabyData, "<ows:ExceptionReport") != nullptr)
     513             :     {
     514           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
     515             :                  pabyData);
     516           1 :         CPLHTTPDestroyResult(psResult);
     517           1 :         return nullptr;
     518             :     }
     519             :     // CPLDebug("CSW", "%s", (const char*)pabyData);
     520             : 
     521          50 :     CPLString osTmpFileName;
     522             : 
     523          25 :     osTmpFileName = m_osTmpDir + "/file.gfs";
     524          25 :     VSIUnlink(osTmpFileName);
     525             : 
     526          25 :     osTmpFileName = m_osTmpDir + "/file.gml";
     527             : 
     528             :     VSILFILE *fp =
     529          25 :         VSIFileFromMemBuffer(osTmpFileName, pabyData, nDataLen, TRUE);
     530          25 :     VSIFCloseL(fp);
     531          25 :     psResult->pabyData = nullptr;
     532             : 
     533          25 :     CPLHTTPDestroyResult(psResult);
     534             : 
     535          25 :     GDALDataset *l_poBaseDS = nullptr;
     536             : 
     537          25 :     if (!poDS->GetOutputSchema().empty())
     538             :     {
     539           5 :         CPLXMLNode *psRoot = CPLParseXMLFile(osTmpFileName);
     540           5 :         if (psRoot == nullptr)
     541             :         {
     542           1 :             if (strstr((const char *)pabyData, "<csw:GetRecordsResponse") ==
     543           1 :                     nullptr &&
     544           1 :                 strstr((const char *)pabyData, "<GetRecordsResponse") ==
     545             :                     nullptr)
     546             :             {
     547           1 :                 if (nDataLen > 1000)
     548           0 :                     pabyData[1000] = 0;
     549           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Error: cannot parse %s",
     550             :                          pabyData);
     551             :             }
     552           2 :             return nullptr;
     553             :         }
     554             :         CPLXMLNode *psSearchResults =
     555           4 :             CPLGetXMLNode(psRoot, "=csw:GetRecordsResponse.csw:SearchResults");
     556           4 :         if (psSearchResults == nullptr)
     557             :         {
     558           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     559             :                      "Cannot find GetRecordsResponse.SearchResults");
     560           1 :             CPLDestroyXMLNode(psRoot);
     561           1 :             return nullptr;
     562             :         }
     563             : 
     564           3 :         l_poBaseDS = MEMDataset::Create("", 0, 0, 0, GDT_Unknown, nullptr);
     565           3 :         OGRLayer *poLyr = l_poBaseDS->CreateLayer("records");
     566           6 :         OGRFieldDefn oField("raw_xml", OFTString);
     567           3 :         CPL_IGNORE_RET_VAL(poLyr->CreateField(&oField));
     568             :         std::unique_ptr<OGRGML_SRSCache, decltype(&OGRGML_SRSCache_Destroy)>
     569           6 :             srsCache{OGRGML_SRSCache_Create(), OGRGML_SRSCache_Destroy};
     570          21 :         for (CPLXMLNode *psIter = psSearchResults->psChild; psIter;
     571          18 :              psIter = psIter->psNext)
     572             :         {
     573          18 :             if (psIter->eType == CXT_Element)
     574             :             {
     575           3 :                 OGRFeature *poFeature = new OGRFeature(poLyr->GetLayerDefn());
     576             : 
     577           3 :                 CPLXMLNode *psNext = psIter->psNext;
     578           3 :                 psIter->psNext = nullptr;
     579           3 :                 char *pszXML = CPLSerializeXMLTree(psIter);
     580             : 
     581           3 :                 const char *pszWest = nullptr;
     582           3 :                 const char *pszEast = nullptr;
     583           3 :                 const char *pszSouth = nullptr;
     584           3 :                 const char *pszNorth = nullptr;
     585             :                 CPLXMLNode *psBBox =
     586           3 :                     CPLSearchXMLNode(psIter, "gmd:EX_GeographicBoundingBox");
     587           3 :                 if (psBBox)
     588             :                 {
     589             :                     /* ISO 19115/19119: http://www.isotc211.org/2005/gmd */
     590           1 :                     pszWest = CPLGetXMLValue(
     591             :                         psBBox, "gmd:westBoundLongitude.gco:Decimal", nullptr);
     592           1 :                     pszEast = CPLGetXMLValue(
     593             :                         psBBox, "gmd:eastBoundLongitude.gco:Decimal", nullptr);
     594           1 :                     pszSouth = CPLGetXMLValue(
     595             :                         psBBox, "gmd:southBoundLatitude.gco:Decimal", nullptr);
     596           1 :                     pszNorth = CPLGetXMLValue(
     597             :                         psBBox, "gmd:northBoundLatitude.gco:Decimal", nullptr);
     598             :                 }
     599           2 :                 else if ((psBBox = CPLSearchXMLNode(psIter, "spdom")) !=
     600             :                          nullptr)
     601             :                 {
     602             :                     /* FGDC: http://www.opengis.net/cat/csw/csdgm */
     603             :                     pszWest =
     604           1 :                         CPLGetXMLValue(psBBox, "bounding.westbc", nullptr);
     605             :                     pszEast =
     606           1 :                         CPLGetXMLValue(psBBox, "bounding.eastbc", nullptr);
     607             :                     pszSouth =
     608           1 :                         CPLGetXMLValue(psBBox, "bounding.southbc", nullptr);
     609             :                     pszNorth =
     610           1 :                         CPLGetXMLValue(psBBox, "bounding.northbc", nullptr);
     611             :                 }
     612           3 :                 if (pszWest && pszEast && pszSouth && pszNorth)
     613             :                 {
     614           2 :                     double dfMinX = CPLAtof(pszWest);
     615           2 :                     double dfMaxX = CPLAtof(pszEast);
     616           2 :                     double dfMinY = CPLAtof(pszSouth);
     617           2 :                     double dfMaxY = CPLAtof(pszNorth);
     618           2 :                     OGRLinearRing *poLR = new OGRLinearRing();
     619           2 :                     poLR->addPoint(dfMinX, dfMinY);
     620           2 :                     poLR->addPoint(dfMinX, dfMaxY);
     621           2 :                     poLR->addPoint(dfMaxX, dfMaxY);
     622           2 :                     poLR->addPoint(dfMaxX, dfMinY);
     623           2 :                     poLR->addPoint(dfMinX, dfMinY);
     624           2 :                     OGRPolygon *poPoly = new OGRPolygon();
     625           2 :                     poPoly->addRingDirectly(poLR);
     626           2 :                     poFeature->SetGeometryDirectly(poPoly);
     627             :                 }
     628           1 :                 else if ((psBBox = CPLSearchXMLNode(
     629           1 :                               psIter, "ows:BoundingBox")) != nullptr)
     630             :                 {
     631           1 :                     CPLFree(psBBox->pszValue);
     632           1 :                     psBBox->pszValue = CPLStrdup("gml:Envelope");
     633           2 :                     CPLString osSRS = CPLGetXMLValue(psBBox, "crs", "");
     634             :                     OGRGeometry *poGeom =
     635           1 :                         GML2OGRGeometry_XMLNode(psBBox, FALSE, srsCache.get(),
     636             :                                                 0, 0, false, true, false);
     637           1 :                     if (poGeom)
     638             :                     {
     639           1 :                         bool bLatLongOrder = true;
     640           1 :                         if (!osSRS.empty())
     641           1 :                             bLatLongOrder = GML_IsSRSLatLongOrder(osSRS);
     642           2 :                         if (bLatLongOrder &&
     643           1 :                             CPLTestBool(CPLGetConfigOption(
     644             :                                 "GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES")))
     645           1 :                             poGeom->swapXY();
     646           1 :                         poFeature->SetGeometryDirectly(poGeom);
     647             :                     }
     648             :                 }
     649             : 
     650           3 :                 psIter->psNext = psNext;
     651             : 
     652           3 :                 poFeature->SetField(0, pszXML);
     653           3 :                 CPL_IGNORE_RET_VAL(poLyr->CreateFeature(poFeature));
     654           3 :                 CPLFree(pszXML);
     655           3 :                 delete poFeature;
     656             :             }
     657             :         }
     658           3 :         CPLDestroyXMLNode(psRoot);
     659             :     }
     660             :     else
     661             :     {
     662          20 :         l_poBaseDS = GDALDataset::Open(osTmpFileName, GDAL_OF_VECTOR);
     663          20 :         if (l_poBaseDS == nullptr)
     664             :         {
     665           2 :             if (strstr((const char *)pabyData, "<csw:GetRecordsResponse") ==
     666           2 :                     nullptr &&
     667           2 :                 strstr((const char *)pabyData, "<GetRecordsResponse") ==
     668             :                     nullptr)
     669             :             {
     670           2 :                 if (nDataLen > 1000)
     671           0 :                     pabyData[1000] = 0;
     672           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Error: cannot parse %s",
     673             :                          pabyData);
     674             :             }
     675           2 :             return nullptr;
     676             :         }
     677             :     }
     678             : 
     679          21 :     OGRLayer *poLayer = l_poBaseDS->GetLayer(0);
     680          21 :     if (poLayer == nullptr)
     681             :     {
     682           5 :         GDALClose(l_poBaseDS);
     683           5 :         return nullptr;
     684             :     }
     685             : 
     686          16 :     return l_poBaseDS;
     687             : }
     688             : 
     689             : /************************************************************************/
     690             : /*                         ISetSpatialFilter()                          */
     691             : /************************************************************************/
     692             : 
     693           2 : OGRErr OGRCSWLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom)
     694             : {
     695           2 :     const OGRErr eErr = OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
     696           2 :     if (eErr == OGRERR_NONE)
     697             :     {
     698           2 :         ResetReading();
     699           2 :         BuildQuery();
     700             :     }
     701           2 :     return eErr;
     702             : }
     703             : 
     704             : /************************************************************************/
     705             : /*                         OGRCSWAddRightPrefixes()                     */
     706             : /************************************************************************/
     707             : 
     708          39 : static void OGRCSWAddRightPrefixes(swq_expr_node *poNode)
     709             : {
     710          39 :     if (poNode->eNodeType == SNT_COLUMN)
     711             :     {
     712           9 :         if (EQUAL(poNode->string_value, "identifier") ||
     713           7 :             EQUAL(poNode->string_value, "title") ||
     714           7 :             EQUAL(poNode->string_value, "type") ||
     715           7 :             EQUAL(poNode->string_value, "subject") ||
     716           7 :             EQUAL(poNode->string_value, "date") ||
     717           7 :             EQUAL(poNode->string_value, "language") ||
     718           7 :             EQUAL(poNode->string_value, "rights") ||
     719           7 :             EQUAL(poNode->string_value, "format") ||
     720           7 :             EQUAL(poNode->string_value, "creator") ||
     721           7 :             EQUAL(poNode->string_value, "source"))
     722             :         {
     723             :             char *pszNewVal =
     724           2 :                 CPLStrdup(CPLSPrintf("dc:%s", poNode->string_value));
     725           2 :             CPLFree(poNode->string_value);
     726           2 :             poNode->string_value = pszNewVal;
     727             :         }
     728           7 :         else if (EQUAL(poNode->string_value, "references") ||
     729           6 :                  EQUAL(poNode->string_value, "modified") ||
     730           6 :                  EQUAL(poNode->string_value, "abstract"))
     731             :         {
     732             :             char *pszNewVal =
     733           1 :                 CPLStrdup(CPLSPrintf("dct:%s", poNode->string_value));
     734           1 :             CPLFree(poNode->string_value);
     735           1 :             poNode->string_value = pszNewVal;
     736             :         }
     737           6 :         else if (EQUAL(poNode->string_value, "other_identifiers"))
     738             :         {
     739           1 :             CPLFree(poNode->string_value);
     740           1 :             poNode->string_value = CPLStrdup("dc:identifier");
     741             :         }
     742           5 :         else if (EQUAL(poNode->string_value, "other_subjects"))
     743             :         {
     744           1 :             CPLFree(poNode->string_value);
     745           1 :             poNode->string_value = CPLStrdup("dc:subject");
     746             :         }
     747           4 :         else if (EQUAL(poNode->string_value, "other_references"))
     748             :         {
     749           1 :             CPLFree(poNode->string_value);
     750           1 :             poNode->string_value = CPLStrdup("dct:references");
     751             :         }
     752           3 :         else if (EQUAL(poNode->string_value, "other_formats"))
     753             :         {
     754           1 :             CPLFree(poNode->string_value);
     755           1 :             poNode->string_value = CPLStrdup("dc:format");
     756             :         }
     757           2 :         else if (EQUAL(poNode->string_value, "AnyText"))
     758             :         {
     759           1 :             CPLFree(poNode->string_value);
     760           1 :             poNode->string_value = CPLStrdup("csw:AnyText");
     761             :         }
     762           1 :         else if (EQUAL(poNode->string_value, "boundingbox"))
     763             :         {
     764           1 :             CPLFree(poNode->string_value);
     765           1 :             poNode->string_value = CPLStrdup("ows:BoundingBox");
     766             :         }
     767             :     }
     768          30 :     else if (poNode->eNodeType == SNT_OPERATION)
     769             :     {
     770          54 :         for (int i = 0; i < poNode->nSubExprCount; i++)
     771          37 :             OGRCSWAddRightPrefixes(poNode->papoSubExpr[i]);
     772             :     }
     773          39 : }
     774             : 
     775             : /************************************************************************/
     776             : /*                        SetAttributeFilter()                          */
     777             : /************************************************************************/
     778             : 
     779           3 : OGRErr OGRCSWLayer::SetAttributeFilter(const char *pszFilter)
     780             : {
     781           3 :     if (pszFilter != nullptr && pszFilter[0] == 0)
     782           0 :         pszFilter = nullptr;
     783             : 
     784           3 :     CPLFree(m_pszAttrQueryString);
     785           3 :     m_pszAttrQueryString = (pszFilter) ? CPLStrdup(pszFilter) : nullptr;
     786             : 
     787           3 :     delete m_poAttrQuery;
     788           3 :     m_poAttrQuery = nullptr;
     789             : 
     790           3 :     if (pszFilter != nullptr)
     791             :     {
     792           2 :         m_poAttrQuery = new OGRFeatureQuery();
     793             : 
     794           2 :         OGRErr eErr = m_poAttrQuery->Compile(GetLayerDefn(), pszFilter, TRUE,
     795             :                                              WFSGetCustomFuncRegistrar());
     796           2 :         if (eErr != OGRERR_NONE)
     797             :         {
     798           0 :             delete m_poAttrQuery;
     799           0 :             m_poAttrQuery = nullptr;
     800           0 :             return eErr;
     801             :         }
     802             :     }
     803             : 
     804           3 :     if (m_poAttrQuery != nullptr)
     805             :     {
     806           2 :         swq_expr_node *poNode = (swq_expr_node *)m_poAttrQuery->GetSWQExpr();
     807           2 :         swq_expr_node *poNodeClone = poNode->Clone();
     808           2 :         poNodeClone->ReplaceBetweenByGEAndLERecurse();
     809           2 :         OGRCSWAddRightPrefixes(poNodeClone);
     810             : 
     811           2 :         int bNeedsNullCheck = FALSE;
     812           2 :         if (poNode->field_type != SWQ_BOOLEAN)
     813           0 :             osCSWWhere = "";
     814             :         else
     815           4 :             osCSWWhere = WFS_TurnSQLFilterToOGCFilter(
     816             :                 poNodeClone, nullptr, nullptr, 110, FALSE, FALSE, FALSE,
     817           2 :                 "ogc:", &bNeedsNullCheck);
     818           2 :         delete poNodeClone;
     819             :     }
     820             :     else
     821           1 :         osCSWWhere = "";
     822             : 
     823           3 :     if (m_poAttrQuery != nullptr && osCSWWhere.empty())
     824             :     {
     825           0 :         CPLDebug("CSW", "Using client-side only mode for filter \"%s\"",
     826             :                  pszFilter);
     827           0 :         OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
     828           0 :         if (eErr != OGRERR_NONE)
     829           0 :             return eErr;
     830             :     }
     831             : 
     832           3 :     ResetReading();
     833           3 :     BuildQuery();
     834             : 
     835           3 :     return OGRERR_NONE;
     836             : }
     837             : 
     838             : /************************************************************************/
     839             : /*                                BuildQuery()                          */
     840             : /************************************************************************/
     841             : 
     842           5 : void OGRCSWLayer::BuildQuery()
     843             : {
     844           5 :     if (m_poFilterGeom != nullptr || !osCSWWhere.empty())
     845             :     {
     846           4 :         osQuery = "<csw:Constraint version=\"1.1.0\">";
     847           4 :         osQuery += "<ogc:Filter>";
     848           4 :         if (m_poFilterGeom != nullptr && !osCSWWhere.empty())
     849           2 :             osQuery += "<ogc:And>";
     850           4 :         if (m_poFilterGeom != nullptr)
     851             :         {
     852           3 :             osQuery += "<ogc:BBOX>";
     853           3 :             osQuery += "<ogc:PropertyName>ows:BoundingBox</ogc:PropertyName>";
     854           3 :             osQuery += "<gml:Envelope srsName=\"urn:ogc:def:crs:EPSG::4326\">";
     855           3 :             OGREnvelope sEnvelope;
     856           3 :             m_poFilterGeom->getEnvelope(&sEnvelope);
     857           3 :             if (CPLTestBool(CPLGetConfigOption(
     858             :                     "GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES")))
     859             :             {
     860             :                 osQuery +=
     861             :                     CPLSPrintf("<gml:lowerCorner>%.16g %.16g</gml:lowerCorner>",
     862           3 :                                sEnvelope.MinY, sEnvelope.MinX);
     863             :                 osQuery +=
     864             :                     CPLSPrintf("<gml:upperCorner>%.16g %.16g</gml:upperCorner>",
     865           3 :                                sEnvelope.MaxY, sEnvelope.MaxX);
     866             :             }
     867             :             else
     868             :             {
     869             :                 osQuery +=
     870             :                     CPLSPrintf("<gml:lowerCorner>%.16g %.16g</gml:lowerCorner>",
     871           0 :                                sEnvelope.MinX, sEnvelope.MinY);
     872             :                 osQuery +=
     873             :                     CPLSPrintf("<gml:upperCorner>%.16g %.16g</gml:upperCorner>",
     874           0 :                                sEnvelope.MaxX, sEnvelope.MaxY);
     875             :             }
     876           3 :             osQuery += "</gml:Envelope>";
     877           3 :             osQuery += "</ogc:BBOX>";
     878             :         }
     879           4 :         osQuery += osCSWWhere;
     880           4 :         if (m_poFilterGeom != nullptr && !osCSWWhere.empty())
     881           2 :             osQuery += "</ogc:And>";
     882           4 :         osQuery += "</ogc:Filter>";
     883           4 :         osQuery += "</csw:Constraint>";
     884             :     }
     885             :     else
     886           1 :         osQuery = "";
     887           5 : }
     888             : 
     889             : /************************************************************************/
     890             : /*                          OGRCSWDataSource()                          */
     891             : /************************************************************************/
     892             : 
     893          11 : OGRCSWDataSource::OGRCSWDataSource()
     894          11 :     : nMaxRecords(500), poLayer(nullptr), bFullExtentRecordsAsNonSpatial(false)
     895             : {
     896          11 : }
     897             : 
     898             : /************************************************************************/
     899             : /*                         ~OGRCSWDataSource()                          */
     900             : /************************************************************************/
     901             : 
     902          22 : OGRCSWDataSource::~OGRCSWDataSource()
     903             : {
     904          11 :     delete poLayer;
     905          22 : }
     906             : 
     907             : /************************************************************************/
     908             : /*                          SendGetCapabilities()                       */
     909             : /************************************************************************/
     910             : 
     911          11 : CPLHTTPResult *OGRCSWDataSource::SendGetCapabilities()
     912             : {
     913          22 :     CPLString osURL(osBaseURL);
     914             : 
     915          11 :     osURL = CPLURLAddKVP(osURL, "SERVICE", "CSW");
     916          11 :     osURL = CPLURLAddKVP(osURL, "REQUEST", "GetCapabilities");
     917             : 
     918          11 :     CPLDebug("CSW", "%s", osURL.c_str());
     919             : 
     920          11 :     CPLHTTPResult *psResult = HTTPFetch(osURL, nullptr);
     921          11 :     if (psResult == nullptr)
     922             :     {
     923           3 :         return nullptr;
     924             :     }
     925             : 
     926           8 :     if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
     927           7 :             nullptr ||
     928           7 :         strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
     929           7 :             nullptr ||
     930           7 :         strstr((const char *)psResult->pabyData, "<ExceptionReport") != nullptr)
     931             :     {
     932           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
     933             :                  psResult->pabyData);
     934           1 :         CPLHTTPDestroyResult(psResult);
     935           1 :         return nullptr;
     936             :     }
     937             : 
     938           7 :     return psResult;
     939             : }
     940             : 
     941             : /************************************************************************/
     942             : /*                                Open()                                */
     943             : /************************************************************************/
     944             : 
     945          11 : int OGRCSWDataSource::Open(const char *pszFilename, char **papszOpenOptionsIn)
     946             : {
     947          11 :     const char *pszBaseURL = CSLFetchNameValue(papszOpenOptionsIn, "URL");
     948          11 :     if (pszBaseURL == nullptr)
     949             :     {
     950          11 :         pszBaseURL = pszFilename;
     951          11 :         if (STARTS_WITH_CI(pszFilename, "CSW:"))
     952          11 :             pszBaseURL += 4;
     953          11 :         if (pszBaseURL[0] == '\0')
     954             :         {
     955           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing URL open option");
     956           0 :             return FALSE;
     957             :         }
     958             :     }
     959          11 :     osBaseURL = pszBaseURL;
     960             :     osElementSetName =
     961          11 :         CSLFetchNameValueDef(papszOpenOptionsIn, "ELEMENTSETNAME", "full");
     962          11 :     bFullExtentRecordsAsNonSpatial = CPLFetchBool(
     963             :         papszOpenOptionsIn, "FULL_EXTENT_RECORDS_AS_NON_SPATIAL", false);
     964             :     osOutputSchema =
     965          11 :         CSLFetchNameValueDef(papszOpenOptionsIn, "OUTPUT_SCHEMA", "");
     966          11 :     if (EQUAL(osOutputSchema, "gmd"))
     967           1 :         osOutputSchema = "http://www.isotc211.org/2005/gmd";
     968          10 :     else if (EQUAL(osOutputSchema, "csw"))
     969           1 :         osOutputSchema = "http://www.opengis.net/cat/csw/2.0.2";
     970          11 :     nMaxRecords =
     971          11 :         atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_RECORDS", "500"));
     972             : 
     973          11 :     if (!STARTS_WITH(osBaseURL, "http://") &&
     974          22 :         !STARTS_WITH(osBaseURL, "https://") &&
     975          11 :         !STARTS_WITH(osBaseURL, "/vsimem/"))
     976           0 :         return FALSE;
     977             : 
     978          11 :     CPLHTTPResult *psResult = SendGetCapabilities();
     979          11 :     if (psResult == nullptr)
     980           4 :         return FALSE;
     981             : 
     982           7 :     CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
     983           7 :     if (psXML == nullptr)
     984             :     {
     985           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
     986             :                  psResult->pabyData);
     987           1 :         CPLHTTPDestroyResult(psResult);
     988           1 :         return FALSE;
     989             :     }
     990           6 :     CPLStripXMLNamespace(psXML, nullptr, TRUE);
     991           6 :     CPLHTTPDestroyResult(psResult);
     992           6 :     psResult = nullptr;
     993             : 
     994             :     const char *pszVersion =
     995           6 :         CPLGetXMLValue(psXML, "=Capabilities.version", nullptr);
     996           6 :     if (pszVersion == nullptr)
     997             :     {
     998           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     999             :                  "Cannot find Capabilities.version");
    1000           2 :         CPLDestroyXMLNode(psXML);
    1001           2 :         return FALSE;
    1002             :     }
    1003           4 :     if (!EQUAL(pszVersion, "2.0.2"))
    1004           0 :         CPLDebug(
    1005             :             "CSW",
    1006             :             "Presumably only work properly with 2.0.2. Reported version is %s",
    1007             :             pszVersion);
    1008           4 :     osVersion = pszVersion;
    1009           4 :     CPLDestroyXMLNode(psXML);
    1010             : 
    1011           4 :     poLayer = new OGRCSWLayer(this);
    1012             : 
    1013           4 :     return TRUE;
    1014             : }
    1015             : 
    1016             : /************************************************************************/
    1017             : /*                              GetLayer()                              */
    1018             : /************************************************************************/
    1019             : 
    1020           6 : const OGRLayer *OGRCSWDataSource::GetLayer(int iLayer) const
    1021             : 
    1022             : {
    1023           6 :     if (iLayer < 0 || iLayer >= ((poLayer != nullptr) ? 1 : 0))
    1024           2 :         return nullptr;
    1025             :     else
    1026           4 :         return poLayer;
    1027             : }
    1028             : 
    1029             : /************************************************************************/
    1030             : /*                            HTTPFetch()                               */
    1031             : /************************************************************************/
    1032             : 
    1033          50 : CPLHTTPResult *OGRCSWDataSource::HTTPFetch(const char *pszURL,
    1034             :                                            const char *pszPost)
    1035             : {
    1036          50 :     char **papszOptions = nullptr;
    1037          50 :     if (pszPost)
    1038             :     {
    1039          39 :         papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", pszPost);
    1040             :         papszOptions =
    1041          39 :             CSLAddNameValue(papszOptions, "HEADERS",
    1042             :                             "Content-Type: application/xml; charset=UTF-8");
    1043             :     }
    1044          50 :     CPLHTTPResult *psResult = CPLHTTPFetch(pszURL, papszOptions);
    1045          50 :     CSLDestroy(papszOptions);
    1046             : 
    1047          50 :     if (psResult == nullptr)
    1048             :     {
    1049           0 :         return nullptr;
    1050             :     }
    1051          50 :     if (psResult->nStatus != 0 || psResult->pszErrBuf != nullptr)
    1052             :     {
    1053           8 :         CPLError(CE_Failure, CPLE_AppDefined,
    1054             :                  "Error returned by server : %s (%d)",
    1055           8 :                  (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown",
    1056             :                  psResult->nStatus);
    1057           8 :         CPLHTTPDestroyResult(psResult);
    1058           8 :         return nullptr;
    1059             :     }
    1060          42 :     if (psResult->pabyData == nullptr)
    1061             :     {
    1062           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    1063             :                  "Empty content returned by server");
    1064           3 :         CPLHTTPDestroyResult(psResult);
    1065           3 :         return nullptr;
    1066             :     }
    1067          39 :     return psResult;
    1068             : }
    1069             : 
    1070             : /************************************************************************/
    1071             : /*                             Identify()                               */
    1072             : /************************************************************************/
    1073             : 
    1074       53684 : static int OGRCSWDriverIdentify(GDALOpenInfo *poOpenInfo)
    1075             : 
    1076             : {
    1077       53684 :     return STARTS_WITH_CI(poOpenInfo->pszFilename, "CSW:");
    1078             : }
    1079             : 
    1080             : /************************************************************************/
    1081             : /*                                Open()                                */
    1082             : /************************************************************************/
    1083             : 
    1084          11 : static GDALDataset *OGRCSWDriverOpen(GDALOpenInfo *poOpenInfo)
    1085             : 
    1086             : {
    1087          11 :     if (!OGRCSWDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update)
    1088           0 :         return nullptr;
    1089             : 
    1090          11 :     OGRCSWDataSource *poDS = new OGRCSWDataSource();
    1091             : 
    1092          11 :     if (!poDS->Open(poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions))
    1093             :     {
    1094           7 :         delete poDS;
    1095           7 :         poDS = nullptr;
    1096             :     }
    1097             : 
    1098          11 :     return poDS;
    1099             : }
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                           RegisterOGRCSW()                           */
    1103             : /************************************************************************/
    1104             : 
    1105        2054 : void RegisterOGRCSW()
    1106             : 
    1107             : {
    1108        2054 :     if (GDALGetDriverByName("CSW") != nullptr)
    1109         283 :         return;
    1110             : 
    1111        1771 :     GDALDriver *poDriver = new GDALDriver();
    1112             : 
    1113        1771 :     poDriver->SetDescription("CSW");
    1114        1771 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    1115        1771 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1116        1771 :                               "OGC CSW (Catalog  Service for the Web)");
    1117        1771 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/csw.html");
    1118             : 
    1119        1771 :     poDriver->SetMetadataItem(GDAL_DMD_CONNECTION_PREFIX, "CSW:");
    1120        1771 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
    1121             : 
    1122        1771 :     poDriver->SetMetadataItem(
    1123             :         GDAL_DMD_OPENOPTIONLIST,
    1124             :         "<OpenOptionList>"
    1125             :         "  <Option name='URL' type='string' description='URL to the CSW server "
    1126             :         "endpoint' required='true'/>"
    1127             :         "  <Option name='ELEMENTSETNAME' type='string-select' "
    1128             :         "description='Level of details of properties' default='full'>"
    1129             :         "    <Value>brief</Value>"
    1130             :         "    <Value>summary</Value>"
    1131             :         "    <Value>full</Value>"
    1132             :         "  </Option>"
    1133             :         "  <Option name='FULL_EXTENT_RECORDS_AS_NON_SPATIAL' type='boolean' "
    1134             :         "description='Whether records with (-180,-90,180,90) extent should be "
    1135             :         "considered non-spatial' default='false'/>"
    1136             :         "  <Option name='OUTPUT_SCHEMA' type='string' description='Value of "
    1137             :         "outputSchema parameter'/>"
    1138             :         "  <Option name='MAX_RECORDS' type='int' description='Maximum number "
    1139             :         "of records to retrieve in a single time' default='500'/>"
    1140        1771 :         "</OpenOptionList>");
    1141             : 
    1142        1771 :     poDriver->pfnIdentify = OGRCSWDriverIdentify;
    1143        1771 :     poDriver->pfnOpen = OGRCSWDriverOpen;
    1144             : 
    1145        1771 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1146             : }

Generated by: LCOV version 1.14