LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/wfs - ogrwfslayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1185 1474 80.4 %
Date: 2025-02-20 10:14:44 Functions: 37 40 92.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WFS Translator
       4             :  * Purpose:  Implements OGRWFSLayer class.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "ogr_wfs.h"
      15             : #include "ogr_api.h"
      16             : #include "cpl_minixml.h"
      17             : #include "cpl_http.h"
      18             : #include "parsexsd.h"
      19             : #include "ogrwfsfilter.h"
      20             : 
      21             : /************************************************************************/
      22             : /*                            OGRWFSLayer()                             */
      23             : /************************************************************************/
      24             : 
      25         209 : OGRWFSLayer::OGRWFSLayer(OGRWFSDataSource *poDSIn, OGRSpatialReference *poSRSIn,
      26             :                          int bAxisOrderAlreadyInvertedIn,
      27             :                          const char *pszBaseURLIn, const char *pszNameIn,
      28         209 :                          const char *pszNSIn, const char *pszNSValIn)
      29             :     : poDS(poDSIn), poFeatureDefn(nullptr), bGotApproximateLayerDefn(false),
      30             :       poGMLFeatureClass(nullptr),
      31             :       bAxisOrderAlreadyInverted(bAxisOrderAlreadyInvertedIn), m_poSRS(poSRSIn),
      32         209 :       pszBaseURL(CPLStrdup(pszBaseURLIn)), pszName(CPLStrdup(pszNameIn)),
      33         209 :       pszNS(pszNSIn ? CPLStrdup(pszNSIn) : nullptr),
      34         209 :       pszNSVal(pszNSValIn ? CPLStrdup(pszNSValIn) : nullptr),
      35             :       bStreamingDS(false), poBaseDS(nullptr), poBaseLayer(nullptr),
      36             :       bHasFetched(false), bReloadNeeded(false), eGeomType(wkbUnknown),
      37             :       nFeatures(-1), bCountFeaturesInGetNextFeature(false),
      38             :       poFetchedFilterGeom(nullptr), nExpectedInserts(0), bInTransaction(false),
      39             :       bUseFeatureIdAtLayerLevel(false), bPagingActive(false),
      40         836 :       nPagingStartIndex(0), nFeatureRead(0), pszRequiredOutputFormat(nullptr)
      41             : {
      42         209 :     SetDescription(pszName);
      43             : 
      44             :     // If changing that, change in the GML driver too
      45         209 :     m_osTmpDir = VSIMemGenerateHiddenFilename("_ogr_wfs_");
      46         209 : }
      47             : 
      48             : /************************************************************************/
      49             : /*                             Clone()                                  */
      50             : /************************************************************************/
      51             : 
      52           0 : OGRWFSLayer *OGRWFSLayer::Clone()
      53             : {
      54             :     OGRWFSLayer *poDupLayer =
      55           0 :         new OGRWFSLayer(poDS, m_poSRS, bAxisOrderAlreadyInverted, pszBaseURL,
      56           0 :                         pszName, pszNS, pszNSVal);
      57           0 :     if (m_poSRS)
      58           0 :         m_poSRS->Reference();
      59           0 :     poDupLayer->poFeatureDefn = GetLayerDefn()->Clone();
      60           0 :     poDupLayer->poFeatureDefn->Reference();
      61           0 :     poDupLayer->bGotApproximateLayerDefn = bGotApproximateLayerDefn;
      62           0 :     poDupLayer->eGeomType = poDupLayer->poFeatureDefn->GetGeomType();
      63           0 :     poDupLayer->pszRequiredOutputFormat =
      64           0 :         pszRequiredOutputFormat ? CPLStrdup(pszRequiredOutputFormat) : nullptr;
      65             : 
      66             :     /* Copy existing schema file if already found */
      67           0 :     CPLString osSrcFileName = CPLSPrintf("%s/file.xsd", m_osTmpDir.c_str());
      68             :     CPLString osTargetFileName =
      69           0 :         CPLSPrintf("%s/file.xsd", poDupLayer->m_osTmpDir.c_str());
      70           0 :     CPL_IGNORE_RET_VAL(CPLCopyFile(osTargetFileName, osSrcFileName));
      71             : 
      72           0 :     return poDupLayer;
      73             : }
      74             : 
      75             : /************************************************************************/
      76             : /*                            ~OGRWFSLayer()                            */
      77             : /************************************************************************/
      78             : 
      79         418 : OGRWFSLayer::~OGRWFSLayer()
      80             : 
      81             : {
      82         209 :     if (bInTransaction)
      83           0 :         OGRWFSLayer::CommitTransaction();
      84             : 
      85         209 :     if (m_poSRS != nullptr)
      86         191 :         m_poSRS->Release();
      87             : 
      88         209 :     if (poFeatureDefn != nullptr)
      89         163 :         poFeatureDefn->Release();
      90         209 :     delete poGMLFeatureClass;
      91             : 
      92         209 :     CPLFree(pszBaseURL);
      93         209 :     CPLFree(pszName);
      94         209 :     CPLFree(pszNS);
      95         209 :     CPLFree(pszNSVal);
      96             : 
      97         209 :     GDALClose(poBaseDS);
      98             : 
      99         209 :     delete poFetchedFilterGeom;
     100             : 
     101         209 :     VSIRmdirRecursive(m_osTmpDir.c_str());
     102             : 
     103         209 :     CPLFree(pszRequiredOutputFormat);
     104         418 : }
     105             : 
     106             : /************************************************************************/
     107             : /*                          SetActiveSRS()                              */
     108             : /************************************************************************/
     109             : 
     110           4 : OGRErr OGRWFSLayer::SetActiveSRS(int /*iGeomField*/,
     111             :                                  const OGRSpatialReference *poSRS)
     112             : {
     113           4 :     if (poSRS == nullptr)
     114           1 :         return OGRERR_FAILURE;
     115           3 :     const char *const apszOptions[] = {
     116             :         "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
     117           3 :     size_t i = 0;
     118           9 :     for (const auto &poSupportedSRS : m_apoSupportedCRSList)
     119             :     {
     120           8 :         if (poSupportedSRS->IsSame(poSRS, apszOptions))
     121             :         {
     122           2 :             m_osSRSName = m_aosSupportedCRSList[i];
     123           2 :             if (m_poSRS)
     124           2 :                 m_poSRS->Release();
     125           2 :             m_poSRS = poSRS->Clone();
     126           2 :             m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     127           2 :             if (poFeatureDefn)
     128             :             {
     129           2 :                 auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
     130           2 :                 if (poGeomFieldDefn)
     131             :                 {
     132           2 :                     poGeomFieldDefn->SetSpatialRef(m_poSRS);
     133             :                 }
     134             :             }
     135           2 :             m_oExtents = OGREnvelope();
     136           2 :             if (m_oWGS84Extents.IsInit())
     137             :             {
     138           4 :                 OGRSpatialReference oWGS84;
     139           2 :                 oWGS84.SetWellKnownGeogCS("WGS84");
     140           2 :                 oWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     141             :                 auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
     142           4 :                     OGRCreateCoordinateTransformation(&oWGS84, m_poSRS));
     143           2 :                 if (poCT)
     144             :                 {
     145           2 :                     poCT->TransformBounds(
     146             :                         m_oWGS84Extents.MinX, m_oWGS84Extents.MinY,
     147             :                         m_oWGS84Extents.MaxX, m_oWGS84Extents.MaxY,
     148             :                         &m_oExtents.MinX, &m_oExtents.MinY, &m_oExtents.MaxX,
     149           2 :                         &m_oExtents.MaxY, 20);
     150             :                 }
     151             :             }
     152           2 :             SetSpatialFilter(nullptr);
     153           2 :             ResetReading();
     154           2 :             return OGRERR_NONE;
     155             :         }
     156           6 :         ++i;
     157             :     }
     158           1 :     return OGRERR_FAILURE;
     159             : }
     160             : 
     161             : /************************************************************************/
     162             : /*                    GetDescribeFeatureTypeURL()                       */
     163             : /************************************************************************/
     164             : 
     165         163 : CPLString OGRWFSLayer::GetDescribeFeatureTypeURL(CPL_UNUSED int bWithNS)
     166             : {
     167         163 :     CPLString osURL(pszBaseURL);
     168         163 :     osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS");
     169         163 :     osURL = CPLURLAddKVP(osURL, "VERSION", poDS->GetVersion());
     170         163 :     osURL = CPLURLAddKVP(osURL, "REQUEST", "DescribeFeatureType");
     171         163 :     osURL = CPLURLAddKVP(osURL, "TYPENAME", WFS_EscapeURL(pszName));
     172         163 :     osURL = CPLURLAddKVP(osURL, "PROPERTYNAME", nullptr);
     173         163 :     osURL = CPLURLAddKVP(osURL, "MAXFEATURES", nullptr);
     174         163 :     osURL = CPLURLAddKVP(osURL, "COUNT", nullptr);
     175         163 :     osURL = CPLURLAddKVP(osURL, "FILTER", nullptr);
     176         326 :     osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT",
     177         163 :                          pszRequiredOutputFormat
     178         163 :                              ? WFS_EscapeURL(pszRequiredOutputFormat).c_str()
     179         163 :                              : nullptr);
     180             : 
     181         163 :     if (pszNS && poDS->GetNeedNAMESPACE())
     182             :     {
     183             :         /* Older Deegree version require NAMESPACE (e.g.
     184             :          * http://www.nokis.org/deegree2/ogcwebservice) */
     185             :         /* This has been now corrected */
     186           0 :         CPLString osValue("xmlns(");
     187           0 :         osValue += pszNS;
     188           0 :         osValue += "=";
     189           0 :         osValue += pszNSVal;
     190           0 :         osValue += ")";
     191           0 :         osURL = CPLURLAddKVP(osURL, "NAMESPACE", WFS_EscapeURL(osValue));
     192             :     }
     193             : 
     194         163 :     return osURL;
     195             : }
     196             : 
     197             : /************************************************************************/
     198             : /*                      DescribeFeatureType()                           */
     199             : /************************************************************************/
     200             : 
     201         101 : OGRFeatureDefn *OGRWFSLayer::DescribeFeatureType()
     202             : {
     203         202 :     CPLString osURL = GetDescribeFeatureTypeURL(TRUE);
     204             : 
     205         101 :     CPLDebug("WFS", "%s", osURL.c_str());
     206             : 
     207         101 :     CPLHTTPResult *psResult = poDS->HTTPFetch(osURL, nullptr);
     208         101 :     if (psResult == nullptr)
     209             :     {
     210          15 :         return nullptr;
     211             :     }
     212             : 
     213          86 :     if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
     214             :         nullptr)
     215             :     {
     216           0 :         if (poDS->IsOldDeegree((const char *)psResult->pabyData))
     217             :         {
     218           0 :             CPLHTTPDestroyResult(psResult);
     219           0 :             return DescribeFeatureType();
     220             :         }
     221           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
     222             :                  psResult->pabyData);
     223           0 :         CPLHTTPDestroyResult(psResult);
     224           0 :         return nullptr;
     225             :     }
     226             : 
     227          86 :     CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
     228          86 :     if (psXML == nullptr)
     229             :     {
     230           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
     231             :                  psResult->pabyData);
     232           1 :         CPLHTTPDestroyResult(psResult);
     233           1 :         return nullptr;
     234             :     }
     235          85 :     CPLHTTPDestroyResult(psResult);
     236             : 
     237          85 :     const CPLXMLNode *psSchema = WFSFindNode(psXML, "schema");
     238          85 :     if (psSchema == nullptr)
     239             :     {
     240           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find <Schema>");
     241           1 :         CPLDestroyXMLNode(psXML);
     242             : 
     243           1 :         return nullptr;
     244             :     }
     245             : 
     246          84 :     OGRFeatureDefn *poFDefn = ParseSchema(psSchema);
     247          84 :     if (poFDefn)
     248          79 :         poDS->SaveLayerSchema(pszName, psSchema);
     249             : 
     250          84 :     CPLDestroyXMLNode(psXML);
     251          84 :     return poFDefn;
     252             : }
     253             : 
     254             : /************************************************************************/
     255             : /*                            ParseSchema()                             */
     256             : /************************************************************************/
     257             : 
     258         146 : OGRFeatureDefn *OGRWFSLayer::ParseSchema(const CPLXMLNode *psSchema)
     259             : {
     260         146 :     osTargetNamespace = CPLGetXMLValue(psSchema, "targetNamespace", "");
     261             : 
     262         292 :     CPLString osTmpFileName;
     263             : 
     264         146 :     osTmpFileName = CPLSPrintf("%s/file.xsd", m_osTmpDir.c_str());
     265         146 :     CPLSerializeXMLTreeToFile(psSchema, osTmpFileName);
     266             : 
     267         292 :     std::vector<GMLFeatureClass *> aosClasses;
     268         146 :     bool bFullyUnderstood = false;
     269         146 :     bool bUseSchemaImports = false;
     270         146 :     bool bHaveSchema = GMLParseXSD(osTmpFileName, bUseSchemaImports, aosClasses,
     271             :                                    bFullyUnderstood);
     272             : 
     273         146 :     if (bHaveSchema && aosClasses.size() == 1)
     274             :     {
     275             :         // CPLDebug("WFS", "Creating %s for %s", osTmpFileName.c_str(),
     276             :         // GetName());
     277         141 :         return BuildLayerDefnFromFeatureClass(aosClasses[0]);
     278             :     }
     279           5 :     else if (bHaveSchema)
     280             :     {
     281             :         std::vector<GMLFeatureClass *>::const_iterator oIter =
     282           0 :             aosClasses.begin();
     283             :         std::vector<GMLFeatureClass *>::const_iterator oEndIter =
     284           0 :             aosClasses.end();
     285           0 :         while (oIter != oEndIter)
     286             :         {
     287           0 :             GMLFeatureClass *poClass = *oIter;
     288           0 :             ++oIter;
     289           0 :             delete poClass;
     290             :         }
     291             :     }
     292             : 
     293           5 :     VSIUnlink(osTmpFileName);
     294             : 
     295           5 :     return nullptr;
     296             : }
     297             : 
     298             : /************************************************************************/
     299             : /*                   BuildLayerDefnFromFeatureClass()                   */
     300             : /************************************************************************/
     301             : 
     302             : OGRFeatureDefn *
     303         141 : OGRWFSLayer::BuildLayerDefnFromFeatureClass(GMLFeatureClass *poClass)
     304             : {
     305         141 :     poGMLFeatureClass = poClass;
     306             : 
     307         141 :     OGRFeatureDefn *poFDefn = new OGRFeatureDefn(pszName);
     308         141 :     poFDefn->SetGeomType(wkbNone);
     309         141 :     if (poGMLFeatureClass->GetGeometryPropertyCount() > 0)
     310             :     {
     311         126 :         poFDefn->SetGeomType(
     312         126 :             (OGRwkbGeometryType)poGMLFeatureClass->GetGeometryProperty(0)
     313         126 :                 ->GetType());
     314         126 :         poFDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
     315             :     }
     316             : 
     317             :     /* -------------------------------------------------------------------- */
     318             :     /*      Added attributes (properties).                                  */
     319             :     /* -------------------------------------------------------------------- */
     320         141 :     if (poDS->ExposeGMLId())
     321             :     {
     322         276 :         OGRFieldDefn oField("gml_id", OFTString);
     323         138 :         oField.SetNullable(FALSE);
     324         138 :         poFDefn->AddFieldDefn(&oField);
     325             :     }
     326             : 
     327         702 :     for (int iField = 0; iField < poGMLFeatureClass->GetPropertyCount();
     328             :          iField++)
     329             :     {
     330         561 :         GMLPropertyDefn *poProperty = poGMLFeatureClass->GetProperty(iField);
     331         561 :         OGRFieldSubType eSubType = OFSTNone;
     332             :         const OGRFieldType eFType =
     333         561 :             GML_GetOGRFieldType(poProperty->GetType(), eSubType);
     334             : 
     335        1122 :         OGRFieldDefn oField(poProperty->GetName(), eFType);
     336         561 :         oField.SetSubType(eSubType);
     337         561 :         if (STARTS_WITH_CI(oField.GetNameRef(), "ogr:"))
     338           0 :             oField.SetName(poProperty->GetName() + 4);
     339         561 :         if (poProperty->GetWidth() > 0)
     340           0 :             oField.SetWidth(poProperty->GetWidth());
     341         561 :         if (poProperty->GetPrecision() > 0)
     342           0 :             oField.SetPrecision(poProperty->GetPrecision());
     343         561 :         if (!poDS->IsEmptyAsNull())
     344           0 :             oField.SetNullable(poProperty->IsNullable());
     345             : 
     346         561 :         poFDefn->AddFieldDefn(&oField);
     347             :     }
     348             : 
     349         141 :     if (poGMLFeatureClass->GetGeometryPropertyCount() > 0)
     350             :     {
     351             :         const char *pszGeometryColumnName =
     352         126 :             poGMLFeatureClass->GetGeometryProperty(0)->GetSrcElement();
     353         126 :         if (pszGeometryColumnName[0] != '\0')
     354             :         {
     355         126 :             osGeometryColumnName = pszGeometryColumnName;
     356         126 :             if (poFDefn->GetGeomFieldCount() > 0)
     357             :             {
     358         252 :                 poFDefn->GetGeomFieldDefn(0)->SetNullable(
     359         126 :                     poGMLFeatureClass->GetGeometryProperty(0)->IsNullable());
     360         126 :                 poFDefn->GetGeomFieldDefn(0)->SetName(pszGeometryColumnName);
     361             :             }
     362             :         }
     363             :     }
     364             : 
     365         141 :     return poFDefn;
     366             : }
     367             : 
     368             : /************************************************************************/
     369             : /*                       MakeGetFeatureURL()                            */
     370             : /************************************************************************/
     371             : 
     372         140 : CPLString OGRWFSLayer::MakeGetFeatureURL(int nRequestMaxFeatures,
     373             :                                          int bRequestHits)
     374             : {
     375         140 :     CPLString osURL(pszBaseURL);
     376         140 :     osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS");
     377         140 :     osURL = CPLURLAddKVP(osURL, "VERSION", poDS->GetVersion());
     378         140 :     osURL = CPLURLAddKVP(osURL, "REQUEST", "GetFeature");
     379         140 :     if (atoi(poDS->GetVersion()) >= 2)
     380          30 :         osURL = CPLURLAddKVP(osURL, "TYPENAMES", WFS_EscapeURL(pszName));
     381             :     else
     382         110 :         osURL = CPLURLAddKVP(osURL, "TYPENAME", WFS_EscapeURL(pszName));
     383         140 :     if (!m_osSRSName.empty())
     384             :         osURL =
     385           1 :             CPLURLAddKVP(osURL, "SRSNAME", WFS_EscapeURL(m_osSRSName.c_str()));
     386         140 :     if (pszRequiredOutputFormat)
     387           0 :         osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT",
     388           0 :                              WFS_EscapeURL(pszRequiredOutputFormat));
     389             : 
     390         140 :     if (poDS->IsPagingAllowed() && !bRequestHits)
     391             :     {
     392          20 :         nRequestMaxFeatures = poDS->GetPageSize();
     393             :         /* If the feature count is known and is less than the page size, we don't
     394             :          * need to do paging. Skipping the pagination parameters improves compatibility
     395             :          * with remote datasources that don't have a primary key.
     396             :          * Without a primary key, the WFS server can't support paging, since there
     397             :          * is no natural sort order defined. */
     398          20 :         if (nFeatures < 0 ||
     399           6 :             (nRequestMaxFeatures && nFeatures > nRequestMaxFeatures))
     400             :         {
     401             :             osURL =
     402          36 :                 CPLURLAddKVP(osURL, "STARTINDEX",
     403          18 :                              CPLSPrintf("%d", nPagingStartIndex +
     404          36 :                                                   poDS->GetBaseStartIndex()));
     405          18 :             bPagingActive = true;
     406             :         }
     407             :     }
     408             : 
     409         140 :     if (nRequestMaxFeatures)
     410             :     {
     411         126 :         osURL = CPLURLAddKVP(
     412          42 :             osURL, atoi(poDS->GetVersion()) >= 2 ? "COUNT" : "MAXFEATURES",
     413          42 :             CPLSPrintf("%d", nRequestMaxFeatures));
     414             :     }
     415         140 :     if (pszNS && poDS->GetNeedNAMESPACE())
     416             :     {
     417             :         /* Older Deegree version require NAMESPACE (e.g.
     418             :          * http://www.nokis.org/deegree2/ogcwebservice) */
     419             :         /* This has been now corrected */
     420           0 :         CPLString osValue("xmlns(");
     421           0 :         osValue += pszNS;
     422           0 :         osValue += "=";
     423           0 :         osValue += pszNSVal;
     424           0 :         osValue += ")";
     425           0 :         osURL = CPLURLAddKVP(osURL, "NAMESPACE", WFS_EscapeURL(osValue));
     426             :     }
     427             : 
     428         140 :     delete poFetchedFilterGeom;
     429         140 :     poFetchedFilterGeom = nullptr;
     430             : 
     431         280 :     CPLString osGeomFilter;
     432             : 
     433         140 :     if (m_poFilterGeom != nullptr && !osGeometryColumnName.empty())
     434             :     {
     435           5 :         OGREnvelope oEnvelope;
     436           5 :         m_poFilterGeom->getEnvelope(&oEnvelope);
     437             : 
     438           5 :         poFetchedFilterGeom = m_poFilterGeom->clone();
     439             : 
     440           5 :         osGeomFilter = "<BBOX>";
     441           5 :         if (atoi(poDS->GetVersion()) >= 2)
     442           0 :             osGeomFilter += "<ValueReference>";
     443             :         else
     444           5 :             osGeomFilter += "<PropertyName>";
     445           5 :         if (pszNS)
     446             :         {
     447           0 :             osGeomFilter += pszNS;
     448           0 :             osGeomFilter += ":";
     449             :         }
     450           5 :         osGeomFilter += osGeometryColumnName;
     451           5 :         if (atoi(poDS->GetVersion()) >= 2)
     452           0 :             osGeomFilter += "</ValueReference>";
     453             :         else
     454           5 :             osGeomFilter += "</PropertyName>";
     455             : 
     456           5 :         if (atoi(poDS->GetVersion()) >= 2)
     457             :         {
     458           0 :             osGeomFilter += "<gml:Envelope";
     459             : 
     460           0 :             CPLString osSRSName = CPLURLGetValue(pszBaseURL, "SRSNAME");
     461           0 :             if (!osSRSName.empty())
     462             :             {
     463           0 :                 osGeomFilter += " srsName=\"";
     464           0 :                 osGeomFilter += osSRSName;
     465           0 :                 osGeomFilter += "\"";
     466             :             }
     467             : 
     468           0 :             osGeomFilter += ">";
     469           0 :             if (bAxisOrderAlreadyInverted)
     470             :             {
     471             :                 osGeomFilter +=
     472             :                     CPLSPrintf("<gml:lowerCorner>%.16f "
     473             :                                "%.16f</gml:lowerCorner><gml:upperCorner>%.16f "
     474             :                                "%.16f</gml:upperCorner>",
     475             :                                oEnvelope.MinY, oEnvelope.MinX, oEnvelope.MaxY,
     476           0 :                                oEnvelope.MaxX);
     477             :             }
     478             :             else
     479             :                 osGeomFilter +=
     480             :                     CPLSPrintf("<gml:lowerCorner>%.16f "
     481             :                                "%.16f</gml:lowerCorner><gml:upperCorner>%.16f "
     482             :                                "%.16f</gml:upperCorner>",
     483             :                                oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX,
     484           0 :                                oEnvelope.MaxY);
     485           0 :             osGeomFilter += "</gml:Envelope>";
     486             :         }
     487           5 :         else if (poDS->RequiresEnvelopeSpatialFilter())
     488             :         {
     489           0 :             osGeomFilter += "<Envelope xmlns=\"http://www.opengis.net/gml\">";
     490           0 :             if (bAxisOrderAlreadyInverted)
     491             :             {
     492             :                 /* We can go here in WFS 1.1 with geographic coordinate systems
     493             :                  */
     494             :                 /* that are natively return in lat,long order, but as we have */
     495             :                 /* presented long,lat order to the user, we must switch back */
     496             :                 /* for the server... */
     497             :                 osGeomFilter +=
     498             :                     CPLSPrintf("<coord><X>%.16f</X><Y>%.16f</Y></"
     499             :                                "coord><coord><X>%.16f</X><Y>%.16f</Y></coord>",
     500             :                                oEnvelope.MinY, oEnvelope.MinX, oEnvelope.MaxY,
     501           0 :                                oEnvelope.MaxX);
     502             :             }
     503             :             else
     504             :                 osGeomFilter +=
     505             :                     CPLSPrintf("<coord><X>%.16f</X><Y>%.16f</Y></"
     506             :                                "coord><coord><X>%.16f</X><Y>%.16f</Y></coord>",
     507             :                                oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX,
     508           0 :                                oEnvelope.MaxY);
     509           0 :             osGeomFilter += "</Envelope>";
     510             :         }
     511             :         else
     512             :         {
     513           5 :             osGeomFilter += "<gml:Box>";
     514           5 :             osGeomFilter += "<gml:coordinates>";
     515           5 :             if (bAxisOrderAlreadyInverted)
     516             :             {
     517             :                 /* We can go here in WFS 1.1 with geographic coordinate systems
     518             :                  */
     519             :                 /* that are natively return in lat,long order, but as we have */
     520             :                 /* presented long,lat order to the user, we must switch back */
     521             :                 /* for the server... */
     522             :                 osGeomFilter +=
     523             :                     CPLSPrintf("%.16f,%.16f %.16f,%.16f", oEnvelope.MinY,
     524           5 :                                oEnvelope.MinX, oEnvelope.MaxY, oEnvelope.MaxX);
     525             :             }
     526             :             else
     527             :                 osGeomFilter +=
     528             :                     CPLSPrintf("%.16f,%.16f %.16f,%.16f", oEnvelope.MinX,
     529           0 :                                oEnvelope.MinY, oEnvelope.MaxX, oEnvelope.MaxY);
     530           5 :             osGeomFilter += "</gml:coordinates>";
     531           5 :             osGeomFilter += "</gml:Box>";
     532             :         }
     533           5 :         osGeomFilter += "</BBOX>";
     534             :     }
     535             : 
     536         140 :     if (!osGeomFilter.empty() || !osWFSWhere.empty())
     537             :     {
     538          33 :         CPLString osFilter;
     539          33 :         if (atoi(poDS->GetVersion()) >= 2)
     540           0 :             osFilter = "<Filter xmlns=\"http://www.opengis.net/fes/2.0\"";
     541             :         else
     542          33 :             osFilter = "<Filter xmlns=\"http://www.opengis.net/ogc\"";
     543          33 :         if (pszNS)
     544             :         {
     545           0 :             osFilter += " xmlns:";
     546           0 :             osFilter += pszNS;
     547           0 :             osFilter += "=\"";
     548           0 :             osFilter += pszNSVal;
     549           0 :             osFilter += "\"";
     550             :         }
     551          33 :         if (atoi(poDS->GetVersion()) >= 2)
     552           0 :             osFilter += " xmlns:gml=\"http://www.opengis.net/gml/3.2\">";
     553             :         else
     554          33 :             osFilter += " xmlns:gml=\"http://www.opengis.net/gml\">";
     555          33 :         if (!osGeomFilter.empty() && !osWFSWhere.empty())
     556           2 :             osFilter += "<And>";
     557          33 :         osFilter += osWFSWhere;
     558          33 :         osFilter += osGeomFilter;
     559          33 :         if (!osGeomFilter.empty() && !osWFSWhere.empty())
     560           2 :             osFilter += "</And>";
     561          33 :         osFilter += "</Filter>";
     562             : 
     563          33 :         osURL = CPLURLAddKVP(osURL, "FILTER", WFS_EscapeURL(osFilter));
     564             :     }
     565             : 
     566         140 :     if (bRequestHits)
     567             :     {
     568           9 :         osURL = CPLURLAddKVP(osURL, "RESULTTYPE", "hits");
     569             :     }
     570         131 :     else if (!aoSortColumns.empty())
     571             :     {
     572           0 :         CPLString osSortBy;
     573           0 :         for (int i = 0; i < (int)aoSortColumns.size(); i++)
     574             :         {
     575           0 :             if (i > 0)
     576           0 :                 osSortBy += ",";
     577           0 :             osSortBy += aoSortColumns[i].osColumn;
     578           0 :             if (!aoSortColumns[i].bAsc)
     579             :             {
     580           0 :                 if (atoi(poDS->GetVersion()) >= 2)
     581           0 :                     osSortBy += " DESC";
     582             :                 else
     583           0 :                     osSortBy += " D";
     584             :             }
     585             :         }
     586           0 :         osURL = CPLURLAddKVP(osURL, "SORTBY", WFS_EscapeURL(osSortBy));
     587             :     }
     588             : 
     589             :     /* If no PROPERTYNAME is specified, build one if there are ignored fields */
     590         280 :     CPLString osPropertyName = CPLURLGetValue(osURL, "PROPERTYNAME");
     591         140 :     const char *pszPropertyName = osPropertyName.c_str();
     592         140 :     if (pszPropertyName[0] == 0 && poFeatureDefn != nullptr)
     593             :     {
     594         131 :         bool bHasIgnoredField = false;
     595         131 :         osPropertyName.clear();
     596         887 :         for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
     597             :         {
     598         756 :             if (EQUAL(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
     599             :                       "gml_id"))
     600             :             {
     601             :                 /* fake field : skip it */
     602             :             }
     603         652 :             else if (poFeatureDefn->GetFieldDefn(iField)->IsIgnored())
     604             :             {
     605          24 :                 bHasIgnoredField = true;
     606             :             }
     607             :             else
     608             :             {
     609         628 :                 if (!osPropertyName.empty())
     610         528 :                     osPropertyName += ",";
     611             :                 osPropertyName +=
     612         628 :                     poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
     613             :             }
     614             :         }
     615         131 :         if (!osGeometryColumnName.empty())
     616             :         {
     617          98 :             if (poFeatureDefn->IsGeometryIgnored())
     618             :             {
     619           0 :                 bHasIgnoredField = true;
     620             :             }
     621             :             else
     622             :             {
     623          98 :                 if (!osPropertyName.empty())
     624          96 :                     osPropertyName += ",";
     625          98 :                 osPropertyName += osGeometryColumnName;
     626             :             }
     627             :         }
     628             : 
     629         131 :         if (bHasIgnoredField && !osPropertyName.empty())
     630             :         {
     631           8 :             osURL = CPLURLAddKVP(osURL, "PROPERTYNAME",
     632          12 :                                  WFS_EscapeURL(osPropertyName));
     633             :         }
     634             :     }
     635             : 
     636         280 :     return osURL;
     637             : }
     638             : 
     639             : /************************************************************************/
     640             : /*               OGRWFSFetchContentDispositionFilename()                */
     641             : /************************************************************************/
     642             : 
     643          48 : static const char *OGRWFSFetchContentDispositionFilename(char **papszHeaders)
     644             : {
     645             :     const char *pszContentDisposition =
     646          48 :         CSLFetchNameValue(papszHeaders, "Content-Disposition");
     647          48 :     if (pszContentDisposition &&
     648           6 :         STARTS_WITH(pszContentDisposition, "attachment; filename="))
     649             :     {
     650           6 :         return pszContentDisposition + strlen("attachment; filename=");
     651             :     }
     652          42 :     return nullptr;
     653             : }
     654             : 
     655             : /************************************************************************/
     656             : /*                  MustRetryIfNonCompliantServer()                     */
     657             : /************************************************************************/
     658             : 
     659          48 : bool OGRWFSLayer::MustRetryIfNonCompliantServer(const char *pszServerAnswer)
     660             : {
     661          48 :     bool bRetry = false;
     662             : 
     663             :     /* Deegree server does not support PropertyIsNotEqualTo */
     664             :     /* We have to turn it into <Not><PropertyIsEqualTo> */
     665          61 :     if (!osWFSWhere.empty() && poDS->PropertyIsNotEqualToSupported() &&
     666          13 :         strstr(pszServerAnswer,
     667             :                "Unknown comparison operation: 'PropertyIsNotEqualTo'") !=
     668             :             nullptr)
     669             :     {
     670           0 :         poDS->SetPropertyIsNotEqualToUnSupported();
     671           0 :         bRetry = true;
     672             :     }
     673             : 
     674             :     /* Deegree server requires the gml: prefix in GmlObjectId element, but ESRI
     675             :      */
     676             :     /* doesn't like it at all ! Other servers don't care... */
     677          61 :     if (!osWFSWhere.empty() && !poDS->DoesGmlObjectIdNeedGMLPrefix() &&
     678          13 :         strstr(pszServerAnswer,
     679             :                "&lt;GmlObjectId&gt; requires 'gml:id'-attribute!") != nullptr)
     680             :     {
     681           0 :         poDS->SetGmlObjectIdNeedsGMLPrefix();
     682           0 :         bRetry = true;
     683             :     }
     684             : 
     685             :     /* GeoServer can return the error 'Only FeatureIds are supported when
     686             :      * encoding id filters to SDE' */
     687          61 :     if (!osWFSWhere.empty() && !bUseFeatureIdAtLayerLevel &&
     688          13 :         strstr(pszServerAnswer, "Only FeatureIds are supported") != nullptr)
     689             :     {
     690           0 :         bUseFeatureIdAtLayerLevel = true;
     691           0 :         bRetry = true;
     692             :     }
     693             : 
     694          48 :     if (bRetry)
     695             :     {
     696           0 :         SetAttributeFilter(osSQLWhere);
     697           0 :         bHasFetched = true;
     698           0 :         bReloadNeeded = false;
     699             :     }
     700             : 
     701          48 :     return bRetry;
     702             : }
     703             : 
     704             : /************************************************************************/
     705             : /*                         FetchGetFeature()                            */
     706             : /************************************************************************/
     707             : 
     708         117 : GDALDataset *OGRWFSLayer::FetchGetFeature(int nRequestMaxFeatures)
     709             : {
     710             : 
     711         234 :     CPLString osURL = MakeGetFeatureURL(nRequestMaxFeatures, FALSE);
     712         117 :     CPLDebug("WFS", "%s", osURL.c_str());
     713             : 
     714         117 :     CPLHTTPResult *psResult = nullptr;
     715             : 
     716         234 :     CPLString osOutputFormat = CPLURLGetValue(osURL, "OUTPUTFORMAT");
     717             : 
     718          80 :     const auto ReadNumberMatched = [this](const char *pszData)
     719             :     {
     720          72 :         const char *pszNumberMatched = strstr(pszData, " numberMatched=\"");
     721          72 :         if (!pszNumberMatched)
     722          69 :             pszNumberMatched = strstr(pszData, "\n"
     723             :                                                "numberMatched=\"");
     724          72 :         if (pszNumberMatched)
     725             :         {
     726          15 :             pszNumberMatched += strlen(" numberMatched=\"");
     727          15 :             if (*pszNumberMatched >= '0' && *pszNumberMatched <= '9')
     728             :             {
     729           2 :                 m_nNumberMatched = CPLAtoGIntBig(pszNumberMatched);
     730           2 :                 CPLDebug("WFS", "numberMatched = " CPL_FRMT_GIB,
     731             :                          m_nNumberMatched);
     732           2 :                 if (!bCountFeaturesInGetNextFeature)
     733           2 :                     nFeatures = m_nNumberMatched;
     734             :             }
     735             :         }
     736         189 :     };
     737             : 
     738         117 :     if (CPLTestBool(CPLGetConfigOption("OGR_WFS_USE_STREAMING", "YES")))
     739             :     {
     740          66 :         CPLString osStreamingName;
     741         129 :         if (STARTS_WITH(osURL, "/vsimem/") &&
     742          63 :             CPLTestBool(CPLGetConfigOption("CPL_CURL_ENABLE_VSIMEM", "FALSE")))
     743             :         {
     744          63 :             osStreamingName = osURL;
     745             :         }
     746             :         else
     747             :         {
     748           3 :             osStreamingName += "/vsicurl_streaming/";
     749           3 :             osStreamingName += osURL;
     750             :         }
     751             : 
     752             :         /* Try streaming when the output format is GML and that we have a .xsd
     753             :          */
     754             :         /* that we are able to understand */
     755          66 :         CPLString osXSDFileName = CPLSPrintf("%s/file.xsd", m_osTmpDir.c_str());
     756             :         VSIStatBufL sBuf;
     757          66 :         GDALDriver *poDriver = nullptr;
     758          66 :         if ((osOutputFormat.empty() ||
     759           6 :              osOutputFormat.ifind("GML") != std::string::npos) &&
     760         104 :             VSIStatL(osXSDFileName, &sBuf) == 0 &&
     761          44 :             (poDriver = GDALDriver::FromHandle(GDALGetDriverByName("GML"))) !=
     762         132 :                 nullptr &&
     763          44 :             poDriver->pfnOpen)
     764             :         {
     765          44 :             bStreamingDS = true;
     766          44 :             CPLStringList aosOptions;
     767          44 :             aosOptions.SetNameValue("XSD", osXSDFileName.c_str());
     768             :             aosOptions.SetNameValue("EMPTY_AS_NULL",
     769          44 :                                     poDS->IsEmptyAsNull() ? "YES" : "NO");
     770          44 :             if (CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG",
     771          44 :                                    nullptr) == nullptr)
     772             :             {
     773             :                 aosOptions.SetNameValue(
     774             :                     "INVERT_AXIS_ORDER_IF_LAT_LONG",
     775          44 :                     poDS->InvertAxisOrderIfLatLong() ? "YES" : "NO");
     776             :             }
     777          44 :             if (CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", nullptr) ==
     778             :                 nullptr)
     779             :             {
     780             :                 aosOptions.SetNameValue("CONSIDER_EPSG_AS_URN",
     781          44 :                                         poDS->GetConsiderEPSGAsURN().c_str());
     782             :             }
     783          44 :             if (CPLGetConfigOption("GML_EXPOSE_GML_ID", nullptr) == nullptr)
     784             :             {
     785             :                 aosOptions.SetNameValue("EXPOSE_GML_ID",
     786          44 :                                         poDS->ExposeGMLId() ? "YES" : "NO");
     787             :                 // iGMLOOIdex ++;
     788             :             }
     789             : 
     790          44 :             GDALOpenInfo oOpenInfo(osStreamingName.c_str(), GA_ReadOnly);
     791          44 :             if (oOpenInfo.nHeaderBytes && m_nNumberMatched < 0)
     792             :             {
     793          29 :                 const char *pszData =
     794             :                     reinterpret_cast<const char *>(oOpenInfo.pabyHeader);
     795          29 :                 ReadNumberMatched(pszData);
     796             :             }
     797          44 :             oOpenInfo.papszOpenOptions = aosOptions.List();
     798             : 
     799          44 :             auto poOutputDS = poDriver->Open(&oOpenInfo, true);
     800          44 :             if (poOutputDS)
     801             :             {
     802          29 :                 return poOutputDS;
     803             :             }
     804             :         }
     805             :         /* Try streaming when the output format is FlatGeobuf */
     806          22 :         else if ((osOutputFormat.empty() ||
     807           6 :                   osOutputFormat.ifind("flatgeobuf") != std::string::npos) &&
     808          29 :                  VSIStatL(osXSDFileName, &sBuf) == 0 &&
     809           1 :                  GDALGetDriverByName("FlatGeobuf") != nullptr)
     810             :         {
     811           1 :             bStreamingDS = true;
     812           1 :             const char *const apszAllowedDrivers[] = {"FlatGeobuf", nullptr};
     813             : 
     814             :             GDALDataset *poFlatGeobuf_DS =
     815           1 :                 (GDALDataset *)GDALOpenEx(osStreamingName, GDAL_OF_VECTOR,
     816             :                                           apszAllowedDrivers, nullptr, nullptr);
     817           1 :             if (poFlatGeobuf_DS)
     818             :             {
     819           1 :                 return poFlatGeobuf_DS;
     820             :             }
     821             :         }
     822             :         else
     823             :         {
     824          21 :             bStreamingDS = false;
     825             :         }
     826             : 
     827          36 :         if (bStreamingDS)
     828             :         {
     829             :             /* In case of failure, read directly the content to examine */
     830             :             /* it, if it is XML error content */
     831             :             char szBuffer[2048];
     832          15 :             int nRead = 0;
     833          15 :             VSILFILE *fp = VSIFOpenL(osStreamingName, "rb");
     834          15 :             if (fp)
     835             :             {
     836           2 :                 nRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp);
     837           2 :                 szBuffer[nRead] = '\0';
     838           2 :                 VSIFCloseL(fp);
     839             :             }
     840             : 
     841          15 :             if (nRead != 0)
     842             :             {
     843           2 :                 if (MustRetryIfNonCompliantServer(szBuffer))
     844           1 :                     return FetchGetFeature(nRequestMaxFeatures);
     845             : 
     846           2 :                 if (strstr(szBuffer, "<ServiceExceptionReport") != nullptr ||
     847           1 :                     strstr(szBuffer, "<ows:ExceptionReport") != nullptr)
     848             :                 {
     849           1 :                     if (poDS->IsOldDeegree(szBuffer))
     850             :                     {
     851           0 :                         return FetchGetFeature(nRequestMaxFeatures);
     852             :                     }
     853             : 
     854           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     855             :                              "Error returned by server : %s", szBuffer);
     856           1 :                     return nullptr;
     857             :                 }
     858             :             }
     859             :         }
     860             :     }
     861             : 
     862          86 :     bStreamingDS = false;
     863          86 :     psResult = poDS->HTTPFetch(osURL, nullptr);
     864          86 :     if (psResult == nullptr)
     865             :     {
     866          40 :         return nullptr;
     867             :     }
     868             : 
     869          46 :     const char *pszContentType = "";
     870          46 :     if (psResult->pszContentType)
     871           6 :         pszContentType = psResult->pszContentType;
     872             : 
     873          46 :     VSIMkdir(m_osTmpDir.c_str(), 0);
     874             : 
     875          46 :     GByte *pabyData = psResult->pabyData;
     876          46 :     int nDataLen = psResult->nDataLen;
     877          46 :     bool bIsMultiPart = false;
     878          46 :     const char *pszAttachmentFilename = nullptr;
     879             : 
     880          52 :     if (strstr(pszContentType, "multipart") &&
     881           6 :         CPLHTTPParseMultipartMime(psResult))
     882             :     {
     883           6 :         bIsMultiPart = true;
     884           6 :         VSIRmdirRecursive(m_osTmpDir.c_str());
     885           6 :         VSIMkdir(m_osTmpDir.c_str(), 0);
     886          14 :         for (int i = 0; i < psResult->nMimePartCount; i++)
     887             :         {
     888          16 :             CPLString osTmpFileName = m_osTmpDir + "/";
     889          16 :             pszAttachmentFilename = OGRWFSFetchContentDispositionFilename(
     890           8 :                 psResult->pasMimePart[i].papszHeaders);
     891             : 
     892           8 :             if (pszAttachmentFilename)
     893           6 :                 osTmpFileName += pszAttachmentFilename;
     894             :             else
     895           2 :                 osTmpFileName += CPLSPrintf("file_%d", i);
     896             : 
     897             :             GByte *pData =
     898           8 :                 (GByte *)VSI_MALLOC_VERBOSE(psResult->pasMimePart[i].nDataLen);
     899           8 :             if (pData)
     900             :             {
     901           8 :                 memcpy(pData, psResult->pasMimePart[i].pabyData,
     902           8 :                        psResult->pasMimePart[i].nDataLen);
     903           8 :                 VSILFILE *fp = VSIFileFromMemBuffer(
     904           8 :                     osTmpFileName, pData, psResult->pasMimePart[i].nDataLen,
     905             :                     TRUE);
     906           8 :                 VSIFCloseL(fp);
     907             :             }
     908             :         }
     909             :     }
     910             :     else
     911             :         pszAttachmentFilename =
     912          40 :             OGRWFSFetchContentDispositionFilename(psResult->papszHeaders);
     913             : 
     914          46 :     bool bJSON = false;
     915          46 :     bool bCSV = false;
     916          46 :     bool bKML = false;
     917          46 :     bool bKMZ = false;
     918          46 :     bool bFlatGeobuf = false;
     919          46 :     bool bZIP = false;
     920          46 :     bool bGZIP = false;
     921             : 
     922          46 :     const char *pszOutputFormat = osOutputFormat.c_str();
     923             : 
     924          92 :     if (FindSubStringInsensitive(pszContentType, "json") ||
     925          46 :         FindSubStringInsensitive(pszOutputFormat, "json"))
     926             :     {
     927           4 :         bJSON = true;
     928             :     }
     929          84 :     else if (FindSubStringInsensitive(pszContentType, "csv") ||
     930          42 :              FindSubStringInsensitive(pszOutputFormat, "csv"))
     931             :     {
     932           0 :         bCSV = true;
     933             :     }
     934          84 :     else if (FindSubStringInsensitive(pszContentType, "kml") ||
     935          42 :              FindSubStringInsensitive(pszOutputFormat, "kml"))
     936             :     {
     937           0 :         bKML = true;
     938             :     }
     939          84 :     else if (FindSubStringInsensitive(pszContentType, "kmz") ||
     940          42 :              FindSubStringInsensitive(pszOutputFormat, "kmz"))
     941             :     {
     942           0 :         bKMZ = true;
     943             :     }
     944          84 :     else if (FindSubStringInsensitive(pszContentType, "flatgeobuf") ||
     945          42 :              FindSubStringInsensitive(pszOutputFormat, "flatgeobuf"))
     946             :     {
     947           0 :         bFlatGeobuf = true;
     948             :     }
     949          42 :     else if (strstr(pszContentType, "application/zip") != nullptr)
     950             :     {
     951           0 :         bZIP = true;
     952             :     }
     953          42 :     else if (strstr(pszContentType, "application/gzip") != nullptr)
     954             :     {
     955           0 :         bGZIP = true;
     956             :     }
     957             : 
     958          46 :     const char *pszData = reinterpret_cast<const char *>(pabyData);
     959          46 :     if (MustRetryIfNonCompliantServer(pszData))
     960             :     {
     961           0 :         CPLHTTPDestroyResult(psResult);
     962           0 :         return FetchGetFeature(nRequestMaxFeatures);
     963             :     }
     964             : 
     965          46 :     if (strstr(pszData, "<ServiceExceptionReport") != nullptr ||
     966          45 :         strstr(pszData, "<ows:ExceptionReport") != nullptr)
     967             :     {
     968           1 :         if (poDS->IsOldDeegree(pszData))
     969             :         {
     970           0 :             CPLHTTPDestroyResult(psResult);
     971           0 :             return FetchGetFeature(nRequestMaxFeatures);
     972             :         }
     973             : 
     974           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
     975             :                  pabyData);
     976           1 :         CPLHTTPDestroyResult(psResult);
     977           1 :         return nullptr;
     978             :     }
     979             : 
     980          45 :     if (m_nNumberMatched < 0)
     981          43 :         ReadNumberMatched(pszData);
     982             : 
     983          90 :     CPLString osTmpFileName;
     984             : 
     985          45 :     if (!bIsMultiPart)
     986             :     {
     987          39 :         if (bJSON)
     988           4 :             osTmpFileName = m_osTmpDir + "/file.geojson";
     989          35 :         else if (bZIP)
     990           0 :             osTmpFileName = m_osTmpDir + "/file.zip";
     991          35 :         else if (bCSV)
     992           0 :             osTmpFileName = m_osTmpDir + "/file.csv";
     993          35 :         else if (bKML)
     994           0 :             osTmpFileName = m_osTmpDir + "/file.kml";
     995          35 :         else if (bKMZ)
     996           0 :             osTmpFileName = m_osTmpDir + "/file.kmz";
     997          35 :         else if (bFlatGeobuf)
     998           0 :             osTmpFileName = m_osTmpDir + "/file.fgb";
     999             :         /* GML is a special case. It needs the .xsd file that has been saved */
    1000             :         /* as file.xsd, so we cannot used the attachment filename */
    1001          35 :         else if (pszAttachmentFilename &&
    1002          35 :                  !EQUAL(CPLGetExtensionSafe(pszAttachmentFilename).c_str(),
    1003             :                         "GML"))
    1004             :         {
    1005           0 :             osTmpFileName = m_osTmpDir + "/";
    1006           0 :             osTmpFileName += pszAttachmentFilename;
    1007             :         }
    1008             :         else
    1009             :         {
    1010          35 :             osTmpFileName = m_osTmpDir + "/file.gfs";
    1011          35 :             VSIUnlink(osTmpFileName);
    1012             : 
    1013          35 :             osTmpFileName = m_osTmpDir + "/file.gml";
    1014             :         }
    1015             : 
    1016             :         VSILFILE *fp =
    1017          39 :             VSIFileFromMemBuffer(osTmpFileName, pabyData, nDataLen, TRUE);
    1018          39 :         VSIFCloseL(fp);
    1019          39 :         psResult->pabyData = nullptr;
    1020             : 
    1021          39 :         if (bZIP)
    1022             :         {
    1023           0 :             osTmpFileName = "/vsizip/" + osTmpFileName;
    1024             :         }
    1025          39 :         else if (bGZIP)
    1026             :         {
    1027           0 :             osTmpFileName = "/vsigzip/" + osTmpFileName;
    1028             :         }
    1029             :     }
    1030             :     else
    1031             :     {
    1032           6 :         pabyData = nullptr;
    1033           6 :         nDataLen = 0;
    1034           6 :         osTmpFileName = m_osTmpDir;
    1035             :     }
    1036             : 
    1037          45 :     CPLHTTPDestroyResult(psResult);
    1038             : 
    1039          45 :     const char *const *papszOpenOptions = nullptr;
    1040          45 :     const char *apszGMLOpenOptions[4] = {nullptr, nullptr, nullptr, nullptr};
    1041          45 :     int iGMLOOIdex = 0;
    1042          45 :     if (CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", nullptr) ==
    1043             :         nullptr)
    1044             :     {
    1045          45 :         apszGMLOpenOptions[iGMLOOIdex] =
    1046          45 :             CPLSPrintf("INVERT_AXIS_ORDER_IF_LAT_LONG=%s",
    1047          45 :                        poDS->InvertAxisOrderIfLatLong() ? "YES" : "NO");
    1048          45 :         iGMLOOIdex++;
    1049             :     }
    1050          45 :     if (CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", nullptr) == nullptr)
    1051             :     {
    1052          45 :         apszGMLOpenOptions[iGMLOOIdex] = CPLSPrintf(
    1053          45 :             "CONSIDER_EPSG_AS_URN=%s", poDS->GetConsiderEPSGAsURN().c_str());
    1054          45 :         iGMLOOIdex++;
    1055             :     }
    1056          45 :     if (CPLGetConfigOption("GML_EXPOSE_GML_ID", nullptr) == nullptr)
    1057             :     {
    1058          45 :         apszGMLOpenOptions[iGMLOOIdex] =
    1059          45 :             CPLSPrintf("EXPOSE_GML_ID=%s", poDS->ExposeGMLId() ? "YES" : "NO");
    1060             :         // iGMLOOIdex ++;
    1061             :     }
    1062             : 
    1063          45 :     GDALDriverH hDrv = GDALIdentifyDriver(osTmpFileName, nullptr);
    1064          45 :     if (hDrv != nullptr && hDrv == GDALGetDriverByName("GML"))
    1065          33 :         papszOpenOptions = apszGMLOpenOptions;
    1066             : 
    1067          45 :     GDALDataset *poPageDS = (GDALDataset *)GDALOpenEx(
    1068             :         osTmpFileName, GDAL_OF_VECTOR, nullptr, papszOpenOptions, nullptr);
    1069          45 :     if (poPageDS == nullptr && (bZIP || bIsMultiPart))
    1070             :     {
    1071           4 :         char **papszFileList = VSIReadDir(osTmpFileName);
    1072           4 :         for (int i = 0; papszFileList != nullptr && papszFileList[i] != nullptr;
    1073             :              i++)
    1074             :         {
    1075             :             const CPLString osFullFilename =
    1076           4 :                 CPLFormFilenameSafe(osTmpFileName, papszFileList[i], nullptr);
    1077           4 :             hDrv = GDALIdentifyDriver(osFullFilename, nullptr);
    1078           4 :             if (hDrv != nullptr && hDrv == GDALGetDriverByName("GML"))
    1079           0 :                 papszOpenOptions = apszGMLOpenOptions;
    1080             :             poPageDS =
    1081           4 :                 (GDALDataset *)GDALOpenEx(osFullFilename, GDAL_OF_VECTOR,
    1082             :                                           nullptr, papszOpenOptions, nullptr);
    1083           4 :             if (poPageDS != nullptr)
    1084           4 :                 break;
    1085             :         }
    1086             : 
    1087           4 :         CSLDestroy(papszFileList);
    1088             :     }
    1089             : 
    1090          45 :     if (poPageDS == nullptr)
    1091             :     {
    1092           2 :         if (pabyData != nullptr && !bJSON && !bZIP &&
    1093           2 :             strstr((const char *)pabyData, "<wfs:FeatureCollection") ==
    1094           2 :                 nullptr &&
    1095           2 :             strstr((const char *)pabyData, "<gml:FeatureCollection") == nullptr)
    1096             :         {
    1097           2 :             if (nDataLen > 1000)
    1098           0 :                 pabyData[1000] = 0;
    1099           2 :             CPLError(CE_Failure, CPLE_AppDefined, "Error: cannot parse %s",
    1100             :                      pabyData);
    1101             :         }
    1102           2 :         return nullptr;
    1103             :     }
    1104             : 
    1105          43 :     OGRLayer *poLayer = poPageDS->GetLayer(0);
    1106          43 :     if (poLayer == nullptr)
    1107             :     {
    1108           0 :         GDALClose(poPageDS);
    1109           0 :         return nullptr;
    1110             :     }
    1111             : 
    1112          43 :     return poPageDS;
    1113             : }
    1114             : 
    1115             : /************************************************************************/
    1116             : /*                            GetLayerDefn()                            */
    1117             : /************************************************************************/
    1118             : 
    1119        2040 : OGRFeatureDefn *OGRWFSLayer::GetLayerDefn()
    1120             : {
    1121        2040 :     if (poFeatureDefn)
    1122        1911 :         return poFeatureDefn;
    1123             : 
    1124         129 :     if (poDS->GetLayerCount() > 1)
    1125             :     {
    1126          53 :         poDS->LoadMultipleLayerDefn(GetName(), pszNS, pszNSVal);
    1127             : 
    1128          53 :         if (poFeatureDefn)
    1129          28 :             return poFeatureDefn;
    1130             :     }
    1131             : 
    1132         101 :     return BuildLayerDefn();
    1133             : }
    1134             : 
    1135             : /************************************************************************/
    1136             : /*                          BuildLayerDefn()                            */
    1137             : /************************************************************************/
    1138             : 
    1139         163 : OGRFeatureDefn *OGRWFSLayer::BuildLayerDefn(OGRFeatureDefn *poSrcFDefn)
    1140             : {
    1141         163 :     bool bUnsetWidthPrecision = false;
    1142             : 
    1143         163 :     poFeatureDefn = new OGRFeatureDefn(pszName);
    1144         163 :     poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
    1145         163 :     poFeatureDefn->Reference();
    1146             : 
    1147         163 :     GDALDataset *l_poDS = nullptr;
    1148             : 
    1149         163 :     if (poSrcFDefn == nullptr)
    1150         101 :         poSrcFDefn = DescribeFeatureType();
    1151         163 :     if (poSrcFDefn == nullptr)
    1152             :     {
    1153          22 :         l_poDS = FetchGetFeature(1);
    1154          22 :         if (l_poDS == nullptr)
    1155             :         {
    1156          20 :             return poFeatureDefn;
    1157             :         }
    1158           2 :         OGRLayer *l_poLayer = l_poDS->GetLayer(0);
    1159           2 :         if (l_poLayer == nullptr)
    1160             :         {
    1161           0 :             return poFeatureDefn;
    1162             :         }
    1163           2 :         poSrcFDefn = l_poLayer->GetLayerDefn();
    1164           2 :         bGotApproximateLayerDefn = true;
    1165             :         /* We cannot trust width and precision based on a single feature */
    1166           2 :         bUnsetWidthPrecision = true;
    1167             :     }
    1168             : 
    1169             :     const CPLStringList aosPropertyName(CSLTokenizeString2(
    1170         143 :         CPLURLGetValue(pszBaseURL, "PROPERTYNAME"), "(,)", 0));
    1171             : 
    1172         143 :     poFeatureDefn->SetGeomType(poSrcFDefn->GetGeomType());
    1173         143 :     if (poSrcFDefn->GetGeomFieldCount() > 0)
    1174         256 :         poFeatureDefn->GetGeomFieldDefn(0)->SetName(
    1175         128 :             poSrcFDefn->GetGeomFieldDefn(0)->GetNameRef());
    1176         858 :     for (int i = 0; i < poSrcFDefn->GetFieldCount(); i++)
    1177             :     {
    1178         715 :         if (!aosPropertyName.empty())
    1179             :         {
    1180           0 :             if (aosPropertyName.FindString(
    1181           0 :                     poSrcFDefn->GetFieldDefn(i)->GetNameRef()) >= 0)
    1182           0 :                 poFeatureDefn->AddFieldDefn(poSrcFDefn->GetFieldDefn(i));
    1183             :             else
    1184           0 :                 bGotApproximateLayerDefn = true;
    1185             :         }
    1186             :         else
    1187             :         {
    1188        1430 :             OGRFieldDefn oFieldDefn(poSrcFDefn->GetFieldDefn(i));
    1189         715 :             if (bUnsetWidthPrecision)
    1190             :             {
    1191          16 :                 oFieldDefn.SetWidth(0);
    1192          16 :                 oFieldDefn.SetPrecision(0);
    1193             :             }
    1194         715 :             poFeatureDefn->AddFieldDefn(&oFieldDefn);
    1195             :         }
    1196             :     }
    1197             : 
    1198         143 :     if (l_poDS)
    1199           2 :         GDALClose(l_poDS);
    1200             :     else
    1201         141 :         delete poSrcFDefn;
    1202             : 
    1203         143 :     return poFeatureDefn;
    1204             : }
    1205             : 
    1206             : /************************************************************************/
    1207             : /*                            ResetReading()                            */
    1208             : /************************************************************************/
    1209             : 
    1210         192 : void OGRWFSLayer::ResetReading()
    1211             : 
    1212             : {
    1213         192 :     if (poFeatureDefn == nullptr)
    1214           7 :         return;
    1215         185 :     if (bPagingActive)
    1216           0 :         bReloadNeeded = true;
    1217         185 :     nPagingStartIndex = 0;
    1218         185 :     nFeatureRead = 0;
    1219         185 :     m_nNumberMatched = -1;
    1220         185 :     m_bHasReadAtLeastOneFeatureInThisPage = false;
    1221         185 :     if (bReloadNeeded)
    1222             :     {
    1223          41 :         GDALClose(poBaseDS);
    1224          41 :         poBaseDS = nullptr;
    1225          41 :         poBaseLayer = nullptr;
    1226          41 :         bHasFetched = false;
    1227          41 :         bReloadNeeded = false;
    1228             :     }
    1229         185 :     if (poBaseLayer)
    1230          47 :         poBaseLayer->ResetReading();
    1231             : }
    1232             : 
    1233             : /************************************************************************/
    1234             : /*                         SetIgnoredFields()                           */
    1235             : /************************************************************************/
    1236             : 
    1237          16 : OGRErr OGRWFSLayer::SetIgnoredFields(CSLConstList papszFields)
    1238             : {
    1239          16 :     bReloadNeeded = true;
    1240          16 :     ResetReading();
    1241          16 :     return OGRLayer::SetIgnoredFields(papszFields);
    1242             : }
    1243             : 
    1244             : /************************************************************************/
    1245             : /*                           GetNextFeature()                           */
    1246             : /************************************************************************/
    1247             : 
    1248         123 : OGRFeature *OGRWFSLayer::GetNextFeature()
    1249             : {
    1250         123 :     GetLayerDefn();
    1251             : 
    1252             :     while (true)
    1253             :     {
    1254         135 :         if (bReloadNeeded)
    1255             :         {
    1256          40 :             m_bHasReadAtLeastOneFeatureInThisPage = false;
    1257          40 :             GDALClose(poBaseDS);
    1258          40 :             poBaseDS = nullptr;
    1259          40 :             poBaseLayer = nullptr;
    1260          40 :             bHasFetched = false;
    1261          40 :             bReloadNeeded = false;
    1262             :         }
    1263         135 :         if (poBaseDS == nullptr && !bHasFetched)
    1264             :         {
    1265          95 :             bHasFetched = true;
    1266          95 :             poBaseDS = FetchGetFeature(0);
    1267          95 :             poBaseLayer = nullptr;
    1268          95 :             if (poBaseDS)
    1269             :             {
    1270          71 :                 poBaseLayer = poBaseDS->GetLayer(0);
    1271          71 :                 if (poBaseLayer == nullptr)
    1272           0 :                     return nullptr;
    1273          71 :                 poBaseLayer->ResetReading();
    1274             : 
    1275             :                 /* Check that the layer field definition is consistent with the
    1276             :                  * one */
    1277             :                 /* we got in BuildLayerDefn() */
    1278          71 :                 if (poFeatureDefn->GetFieldCount() !=
    1279          71 :                     poBaseLayer->GetLayerDefn()->GetFieldCount())
    1280           6 :                     bGotApproximateLayerDefn = true;
    1281             :                 else
    1282             :                 {
    1283         499 :                     for (int iField = 0;
    1284         499 :                          iField < poFeatureDefn->GetFieldCount(); iField++)
    1285             :                     {
    1286             :                         OGRFieldDefn *poFDefn1 =
    1287         440 :                             poFeatureDefn->GetFieldDefn(iField);
    1288             :                         OGRFieldDefn *poFDefn2 =
    1289         440 :                             poBaseLayer->GetLayerDefn()->GetFieldDefn(iField);
    1290         440 :                         if (strcmp(poFDefn1->GetNameRef(),
    1291         874 :                                    poFDefn2->GetNameRef()) != 0 ||
    1292         434 :                             poFDefn1->GetType() != poFDefn2->GetType())
    1293             :                         {
    1294           6 :                             bGotApproximateLayerDefn = true;
    1295           6 :                             break;
    1296             :                         }
    1297             :                     }
    1298             :                 }
    1299             :             }
    1300             :         }
    1301         135 :         if (poBaseDS == nullptr || poBaseLayer == nullptr)
    1302          37 :             return nullptr;
    1303             : 
    1304          98 :         OGRFeature *poSrcFeature = poBaseLayer->GetNextFeature();
    1305          98 :         if (poSrcFeature == nullptr)
    1306             :         {
    1307          22 :             if (bPagingActive && m_bHasReadAtLeastOneFeatureInThisPage &&
    1308          14 :                 (m_nNumberMatched < 0 || nFeatureRead < m_nNumberMatched))
    1309             :             {
    1310          12 :                 bReloadNeeded = true;
    1311          12 :                 nPagingStartIndex = nFeatureRead;
    1312          12 :                 continue;
    1313             :             }
    1314          10 :             return nullptr;
    1315             :         }
    1316          76 :         nFeatureRead++;
    1317          76 :         m_bHasReadAtLeastOneFeatureInThisPage = true;
    1318          76 :         if (bCountFeaturesInGetNextFeature)
    1319           2 :             nFeatures++;
    1320             : 
    1321          76 :         OGRGeometry *poGeom = poSrcFeature->GetGeometryRef();
    1322          82 :         if (m_poFilterGeom != nullptr && poGeom != nullptr &&
    1323           6 :             !FilterGeometry(poGeom))
    1324             :         {
    1325           0 :             delete poSrcFeature;
    1326           0 :             continue;
    1327             :         }
    1328             : 
    1329             :         /* Client-side attribute filtering with underlying layer defn */
    1330             :         /* identical to exposed layer defn. */
    1331          64 :         if (!bGotApproximateLayerDefn && osWFSWhere.empty() &&
    1332         140 :             m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poSrcFeature))
    1333             :         {
    1334           0 :             delete poSrcFeature;
    1335           0 :             continue;
    1336             :         }
    1337             : 
    1338          76 :         OGRFeature *poNewFeature = new OGRFeature(poFeatureDefn);
    1339          76 :         if (bGotApproximateLayerDefn)
    1340             :         {
    1341          12 :             poNewFeature->SetFrom(poSrcFeature);
    1342             : 
    1343             :             /* Client-side attribute filtering. */
    1344          12 :             if (m_poAttrQuery != nullptr && osWFSWhere.empty() &&
    1345           0 :                 !m_poAttrQuery->Evaluate(poNewFeature))
    1346             :             {
    1347           0 :                 delete poSrcFeature;
    1348           0 :                 delete poNewFeature;
    1349           0 :                 continue;
    1350             :             }
    1351             :         }
    1352             :         else
    1353             :         {
    1354         524 :             for (int iField = 0; iField < poFeatureDefn->GetFieldCount();
    1355             :                  iField++)
    1356             :             {
    1357         460 :                 poNewFeature->SetField(iField,
    1358         460 :                                        poSrcFeature->GetRawFieldRef(iField));
    1359             :             }
    1360          64 :             poNewFeature->SetStyleString(poSrcFeature->GetStyleString());
    1361          64 :             poNewFeature->SetGeometryDirectly(poSrcFeature->StealGeometry());
    1362             :         }
    1363          76 :         poNewFeature->SetFID(poSrcFeature->GetFID());
    1364          76 :         poGeom = poNewFeature->GetGeometryRef();
    1365             : 
    1366             :         /* FIXME? I don't really know what we should do with WFS 1.1.0 */
    1367             :         /* and non-GML format !!! I guess 50% WFS servers must do it wrong
    1368             :          * anyway */
    1369             :         /* GeoServer does currently axis inversion for non GML output, but */
    1370             :         /* apparently this is not correct :
    1371             :          * http://jira.codehaus.org/browse/GEOS-3657 */
    1372         116 :         if (poGeom != nullptr && bAxisOrderAlreadyInverted &&
    1373          40 :             strcmp(poBaseDS->GetDriverName(), "GML") != 0)
    1374             :         {
    1375           9 :             poGeom->swapXY();
    1376             :         }
    1377             : 
    1378          76 :         if (poGeom && m_poSRS)
    1379          42 :             poGeom->assignSpatialReference(m_poSRS);
    1380          76 :         delete poSrcFeature;
    1381          76 :         return poNewFeature;
    1382          12 :     }
    1383             : }
    1384             : 
    1385             : /************************************************************************/
    1386             : /*                         ISetSpatialFilter()                          */
    1387             : /************************************************************************/
    1388             : 
    1389          29 : OGRErr OGRWFSLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom)
    1390             : {
    1391          29 :     if (bStreamingDS)
    1392             :     {
    1393           7 :         bReloadNeeded = true;
    1394             :     }
    1395          22 :     else if (poFetchedFilterGeom == nullptr && poBaseDS != nullptr)
    1396             :     {
    1397             :         /* If there was no filter set, and that we set one */
    1398             :         /* the new result set can only be a subset of the whole */
    1399             :         /* so no need to reload from source */
    1400           5 :         bReloadNeeded = false;
    1401             :     }
    1402          17 :     else if (poFetchedFilterGeom != nullptr && poGeom != nullptr &&
    1403           2 :              poBaseDS != nullptr)
    1404             :     {
    1405           1 :         OGREnvelope oOldEnvelope, oNewEnvelope;
    1406           1 :         poFetchedFilterGeom->getEnvelope(&oOldEnvelope);
    1407           1 :         poGeom->getEnvelope(&oNewEnvelope);
    1408             :         /* Optimization : we don't need to request the server */
    1409             :         /* if the new BBOX is inside the old BBOX as we have */
    1410             :         /* already all the features */
    1411           1 :         bReloadNeeded = !oOldEnvelope.Contains(oNewEnvelope);
    1412             :     }
    1413             :     else
    1414             :     {
    1415          16 :         bReloadNeeded = true;
    1416             :     }
    1417          29 :     nFeatures = -1;
    1418          29 :     const OGRErr eErr = OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
    1419          29 :     ResetReading();
    1420          29 :     return eErr;
    1421             : }
    1422             : 
    1423             : /************************************************************************/
    1424             : /*                        SetAttributeFilter()                          */
    1425             : /************************************************************************/
    1426             : 
    1427          90 : OGRErr OGRWFSLayer::SetAttributeFilter(const char *pszFilter)
    1428             : {
    1429          90 :     if (pszFilter != nullptr && pszFilter[0] == 0)
    1430           8 :         pszFilter = nullptr;
    1431             : 
    1432         180 :     CPLString osOldWFSWhere(osWFSWhere);
    1433             : 
    1434          90 :     CPLFree(m_pszAttrQueryString);
    1435          90 :     m_pszAttrQueryString = (pszFilter) ? CPLStrdup(pszFilter) : nullptr;
    1436             : 
    1437          90 :     delete m_poAttrQuery;
    1438          90 :     m_poAttrQuery = nullptr;
    1439             : 
    1440          90 :     if (pszFilter != nullptr)
    1441             :     {
    1442          58 :         m_poAttrQuery = new OGRFeatureQuery();
    1443             : 
    1444          58 :         OGRErr eErr = m_poAttrQuery->Compile(GetLayerDefn(), pszFilter, TRUE,
    1445             :                                              WFSGetCustomFuncRegistrar());
    1446          58 :         if (eErr != OGRERR_NONE)
    1447             :         {
    1448          28 :             delete m_poAttrQuery;
    1449          28 :             m_poAttrQuery = nullptr;
    1450          28 :             return eErr;
    1451             :         }
    1452             :     }
    1453             : 
    1454          62 :     if (poDS->HasMinOperators() && m_poAttrQuery != nullptr)
    1455             :     {
    1456          30 :         swq_expr_node *poNode = (swq_expr_node *)m_poAttrQuery->GetSWQExpr();
    1457          30 :         poNode->ReplaceBetweenByGEAndLERecurse();
    1458             : 
    1459          30 :         int bNeedsNullCheck = FALSE;
    1460          60 :         int nVersion = (strcmp(poDS->GetVersion(), "1.0.0") == 0) ? 100
    1461          30 :                        : (atoi(poDS->GetVersion()) >= 2)          ? 200
    1462          30 :                                                                   : 110;
    1463          30 :         if (poNode->field_type != SWQ_BOOLEAN)
    1464           0 :             osWFSWhere = "";
    1465             :         else
    1466          60 :             osWFSWhere = WFS_TurnSQLFilterToOGCFilter(
    1467             :                 poNode, nullptr, GetLayerDefn(), nVersion,
    1468          30 :                 poDS->PropertyIsNotEqualToSupported(),
    1469          30 :                 poDS->UseFeatureId() || bUseFeatureIdAtLayerLevel,
    1470          60 :                 poDS->DoesGmlObjectIdNeedGMLPrefix(), "", &bNeedsNullCheck);
    1471          30 :         if (bNeedsNullCheck && !poDS->HasNullCheck())
    1472           0 :             osWFSWhere = "";
    1473             :     }
    1474             :     else
    1475          32 :         osWFSWhere = "";
    1476             : 
    1477          62 :     if (m_poAttrQuery != nullptr && osWFSWhere.empty())
    1478             :     {
    1479           0 :         CPLDebug("WFS", "Using client-side only mode for filter \"%s\"",
    1480             :                  pszFilter);
    1481           0 :         OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
    1482           0 :         if (eErr != OGRERR_NONE)
    1483           0 :             return eErr;
    1484             :     }
    1485          62 :     ResetReading();
    1486             : 
    1487          62 :     osSQLWhere = (pszFilter) ? pszFilter : "";
    1488             : 
    1489          62 :     if (osWFSWhere != osOldWFSWhere)
    1490          46 :         bReloadNeeded = true;
    1491             :     else
    1492          16 :         bReloadNeeded = false;
    1493          62 :     nFeatures = -1;
    1494             : 
    1495          62 :     return OGRERR_NONE;
    1496             : }
    1497             : 
    1498             : /************************************************************************/
    1499             : /*                           TestCapability()                           */
    1500             : /************************************************************************/
    1501             : 
    1502         160 : int OGRWFSLayer::TestCapability(const char *pszCap)
    1503             : 
    1504             : {
    1505         160 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    1506             :     {
    1507          17 :         if (nFeatures >= 0)
    1508           0 :             return TRUE;
    1509             : 
    1510           0 :         return poBaseLayer != nullptr && m_poFilterGeom == nullptr &&
    1511           0 :                m_poAttrQuery == nullptr &&
    1512          17 :                poBaseLayer->TestCapability(pszCap) &&
    1513           0 :                (!poDS->IsPagingAllowed() &&
    1514          17 :                 poBaseLayer->GetFeatureCount() < poDS->GetPageSize());
    1515             :     }
    1516             : 
    1517         143 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    1518             :     {
    1519           7 :         if (m_oExtents.IsInit())
    1520           0 :             return TRUE;
    1521             : 
    1522           7 :         return poBaseLayer != nullptr && poBaseLayer->TestCapability(pszCap);
    1523             :     }
    1524             : 
    1525         136 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    1526           0 :         return poBaseLayer != nullptr && poBaseLayer->TestCapability(pszCap);
    1527             : 
    1528         136 :     else if (EQUAL(pszCap, OLCSequentialWrite) ||
    1529          98 :              EQUAL(pszCap, OLCDeleteFeature) || EQUAL(pszCap, OLCRandomWrite))
    1530             :     {
    1531          78 :         GetLayerDefn();
    1532         156 :         return poDS->SupportTransactions() && poDS->UpdateMode() &&
    1533         156 :                poFeatureDefn->GetFieldIndex("gml_id") == 0;
    1534             :     }
    1535          58 :     else if (EQUAL(pszCap, OLCTransactions))
    1536             :     {
    1537          50 :         return poDS->SupportTransactions() && poDS->UpdateMode();
    1538             :     }
    1539           8 :     else if (EQUAL(pszCap, OLCIgnoreFields))
    1540             :     {
    1541           0 :         return TRUE;
    1542             :     }
    1543             : 
    1544           8 :     return FALSE;
    1545             : }
    1546             : 
    1547             : /************************************************************************/
    1548             : /*                  ExecuteGetFeatureResultTypeHits()                   */
    1549             : /************************************************************************/
    1550             : 
    1551           9 : GIntBig OGRWFSLayer::ExecuteGetFeatureResultTypeHits()
    1552             : {
    1553           9 :     char *pabyData = nullptr;
    1554          18 :     CPLString osURL = MakeGetFeatureURL(0, TRUE);
    1555           9 :     if (pszRequiredOutputFormat)
    1556           0 :         osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT",
    1557           0 :                              WFS_EscapeURL(pszRequiredOutputFormat));
    1558           9 :     CPLDebug("WFS", "%s", osURL.c_str());
    1559             : 
    1560           9 :     CPLHTTPResult *psResult = poDS->HTTPFetch(osURL, nullptr);
    1561           9 :     if (psResult == nullptr)
    1562             :     {
    1563           1 :         return -1;
    1564             :     }
    1565             : 
    1566             :     /* http://demo.snowflakesoftware.com:8080/Obstacle_AIXM_ZIP/GOPublisherWFS
    1567             :      * returns */
    1568             :     /* zip content, including for RESULTTYPE=hits */
    1569           8 :     if (psResult->pszContentType != nullptr &&
    1570           0 :         strstr(psResult->pszContentType, "application/zip") != nullptr)
    1571             :     {
    1572             :         const CPLString osTmpFileName(
    1573           0 :             VSIMemGenerateHiddenFilename("wfstemphits.zip"));
    1574           0 :         VSILFILE *fp = VSIFileFromMemBuffer(osTmpFileName, psResult->pabyData,
    1575           0 :                                             psResult->nDataLen, FALSE);
    1576           0 :         VSIFCloseL(fp);
    1577             : 
    1578           0 :         CPLString osZipTmpFileName("/vsizip/" + osTmpFileName);
    1579             : 
    1580           0 :         char **papszDirContent = VSIReadDir(osZipTmpFileName);
    1581           0 :         if (CSLCount(papszDirContent) != 1)
    1582             :         {
    1583           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1584             :                      "Cannot parse result of RESULTTYPE=hits request : more "
    1585             :                      "than one file in zip");
    1586           0 :             CSLDestroy(papszDirContent);
    1587           0 :             CPLHTTPDestroyResult(psResult);
    1588           0 :             VSIUnlink(osTmpFileName);
    1589           0 :             return -1;
    1590             :         }
    1591             : 
    1592           0 :         CPLString osFileInZipTmpFileName = osZipTmpFileName + "/";
    1593           0 :         osFileInZipTmpFileName += papszDirContent[0];
    1594             : 
    1595           0 :         fp = VSIFOpenL(osFileInZipTmpFileName.c_str(), "rb");
    1596             :         VSIStatBufL sBuf;
    1597           0 :         if (fp == nullptr ||
    1598           0 :             VSIStatL(osFileInZipTmpFileName.c_str(), &sBuf) != 0)
    1599             :         {
    1600           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1601             :                      "Cannot parse result of RESULTTYPE=hits request : cannot "
    1602             :                      "open one file in zip");
    1603           0 :             CSLDestroy(papszDirContent);
    1604           0 :             CPLHTTPDestroyResult(psResult);
    1605           0 :             VSIUnlink(osTmpFileName);
    1606           0 :             if (fp)
    1607           0 :                 VSIFCloseL(fp);
    1608           0 :             return -1;
    1609             :         }
    1610           0 :         pabyData = (char *)CPLMalloc((size_t)(sBuf.st_size + 1));
    1611           0 :         pabyData[sBuf.st_size] = 0;
    1612           0 :         VSIFReadL(pabyData, 1, (size_t)sBuf.st_size, fp);
    1613           0 :         VSIFCloseL(fp);
    1614             : 
    1615           0 :         CSLDestroy(papszDirContent);
    1616           0 :         VSIUnlink(osTmpFileName);
    1617             :     }
    1618             :     else
    1619             :     {
    1620           8 :         pabyData = (char *)psResult->pabyData;
    1621           8 :         psResult->pabyData = nullptr;
    1622             :     }
    1623             : 
    1624           8 :     if (strstr(pabyData, "<ServiceExceptionReport") != nullptr ||
    1625           7 :         strstr(pabyData, "<ows:ExceptionReport") != nullptr)
    1626             :     {
    1627           1 :         if (poDS->IsOldDeegree(pabyData))
    1628             :         {
    1629           0 :             CPLHTTPDestroyResult(psResult);
    1630           0 :             return ExecuteGetFeatureResultTypeHits();
    1631             :         }
    1632           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
    1633             :                  pabyData);
    1634           1 :         CPLHTTPDestroyResult(psResult);
    1635           1 :         CPLFree(pabyData);
    1636           1 :         return -1;
    1637             :     }
    1638             : 
    1639           7 :     CPLXMLNode *psXML = CPLParseXMLString(pabyData);
    1640           7 :     if (psXML == nullptr)
    1641             :     {
    1642           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
    1643             :                  pabyData);
    1644           2 :         CPLHTTPDestroyResult(psResult);
    1645           2 :         CPLFree(pabyData);
    1646           2 :         return -1;
    1647             :     }
    1648             : 
    1649           5 :     CPLStripXMLNamespace(psXML, nullptr, TRUE);
    1650           5 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=FeatureCollection");
    1651           5 :     if (psRoot == nullptr)
    1652             :     {
    1653           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1654             :                  "Cannot find <FeatureCollection>");
    1655           1 :         CPLDestroyXMLNode(psXML);
    1656           1 :         CPLHTTPDestroyResult(psResult);
    1657           1 :         CPLFree(pabyData);
    1658           1 :         return -1;
    1659             :     }
    1660             : 
    1661           4 :     const char *pszValue = CPLGetXMLValue(psRoot, "numberOfFeatures", nullptr);
    1662           4 :     if (pszValue == nullptr)
    1663             :         pszValue =
    1664           3 :             CPLGetXMLValue(psRoot, "numberMatched", nullptr); /* WFS 2.0.0 */
    1665           4 :     if (pszValue == nullptr)
    1666             :     {
    1667           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find numberOfFeatures");
    1668           1 :         CPLDestroyXMLNode(psXML);
    1669           1 :         CPLHTTPDestroyResult(psResult);
    1670           1 :         CPLFree(pabyData);
    1671             : 
    1672           1 :         poDS->DisableSupportHits();
    1673           1 :         return -1;
    1674             :     }
    1675             : 
    1676           3 :     GIntBig l_nFeatures = CPLAtoGIntBig(pszValue);
    1677             :     /* Hum,
    1678             :      * http://deegree3-testing.deegree.org:80/deegree-inspire-node/services?MAXFEATURES=10&SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=ad:Address&OUTPUTFORMAT=text/xml;%20subtype=gml/3.2.1&RESULTTYPE=hits
    1679             :      */
    1680             :     /* returns more than MAXFEATURES features... So truncate to MAXFEATURES */
    1681             :     CPLString osMaxFeatures = CPLURLGetValue(
    1682           3 :         osURL, atoi(poDS->GetVersion()) >= 2 ? "COUNT" : "MAXFEATURES");
    1683           3 :     if (!osMaxFeatures.empty())
    1684             :     {
    1685           0 :         GIntBig nMaxFeatures = CPLAtoGIntBig(osMaxFeatures);
    1686           0 :         if (l_nFeatures > nMaxFeatures)
    1687             :         {
    1688           0 :             CPLDebug("WFS",
    1689             :                      "Truncating result from " CPL_FRMT_GIB " to " CPL_FRMT_GIB,
    1690             :                      l_nFeatures, nMaxFeatures);
    1691           0 :             l_nFeatures = nMaxFeatures;
    1692             :         }
    1693             :     }
    1694             : 
    1695           3 :     CPLDestroyXMLNode(psXML);
    1696           3 :     CPLHTTPDestroyResult(psResult);
    1697           3 :     CPLFree(pabyData);
    1698             : 
    1699           3 :     return l_nFeatures;
    1700             : }
    1701             : 
    1702             : /************************************************************************/
    1703             : /*              CanRunGetFeatureCountAndGetExtentTogether()             */
    1704             : /************************************************************************/
    1705             : 
    1706          14 : int OGRWFSLayer::CanRunGetFeatureCountAndGetExtentTogether()
    1707             : {
    1708             :     /* In some cases, we can evaluate the result of GetFeatureCount() */
    1709             :     /* and GetExtent() with the same data */
    1710          14 :     CPLString osRequestURL = MakeGetFeatureURL(0, FALSE);
    1711          22 :     return (!m_oExtents.IsInit() && nFeatures < 0 &&
    1712           8 :             osRequestURL.ifind("FILTER") == std::string::npos &&
    1713           8 :             osRequestURL.ifind("MAXFEATURES") == std::string::npos &&
    1714          30 :             osRequestURL.ifind("COUNT") == std::string::npos &&
    1715          36 :             !(GetLayerDefn()->IsGeometryIgnored()));
    1716             : }
    1717             : 
    1718             : /************************************************************************/
    1719             : /*                           GetFeatureCount()                          */
    1720             : /************************************************************************/
    1721             : 
    1722          12 : GIntBig OGRWFSLayer::GetFeatureCount(int bForce)
    1723             : {
    1724          12 :     if (nFeatures >= 0)
    1725           2 :         return nFeatures;
    1726             : 
    1727          10 :     if (TestCapability(OLCFastFeatureCount))
    1728           0 :         return poBaseLayer->GetFeatureCount(bForce);
    1729             : 
    1730          20 :     if ((m_poAttrQuery == nullptr || !osWFSWhere.empty()) &&
    1731          10 :         poDS->GetFeatureSupportHits())
    1732             :     {
    1733           9 :         nFeatures = ExecuteGetFeatureResultTypeHits();
    1734           9 :         if (nFeatures >= 0)
    1735           3 :             return nFeatures;
    1736             :     }
    1737             : 
    1738             :     /* If we have not yet the base layer, try to read one */
    1739             :     /* feature, and then query again OLCFastFeatureCount on the */
    1740             :     /* base layer. In case the WFS response would contain the */
    1741             :     /* number of features */
    1742           7 :     if (poBaseLayer == nullptr)
    1743             :     {
    1744           7 :         ResetReading();
    1745           7 :         OGRFeature *poFeature = GetNextFeature();
    1746           7 :         delete poFeature;
    1747           7 :         ResetReading();
    1748             : 
    1749           7 :         if (TestCapability(OLCFastFeatureCount))
    1750           0 :             return poBaseLayer->GetFeatureCount(bForce);
    1751             :     }
    1752             : 
    1753             :     /* In some cases, we can evaluate the result of GetFeatureCount() */
    1754             :     /* and GetExtent() with the same data */
    1755           7 :     if (CanRunGetFeatureCountAndGetExtentTogether())
    1756             :     {
    1757           1 :         OGREnvelope sDummy;
    1758           1 :         CPL_IGNORE_RET_VAL(GetExtent(&sDummy));
    1759             :     }
    1760             : 
    1761           7 :     if (nFeatures < 0)
    1762           7 :         nFeatures = OGRLayer::GetFeatureCount(bForce);
    1763             : 
    1764           7 :     return nFeatures;
    1765             : }
    1766             : 
    1767             : /************************************************************************/
    1768             : /*                              SetExtents()                            */
    1769             : /************************************************************************/
    1770             : 
    1771         152 : void OGRWFSLayer::SetExtents(double dfMinXIn, double dfMinYIn, double dfMaxXIn,
    1772             :                              double dfMaxYIn)
    1773             : {
    1774         152 :     m_oExtents.MinX = dfMinXIn;
    1775         152 :     m_oExtents.MinY = dfMinYIn;
    1776         152 :     m_oExtents.MaxX = dfMaxXIn;
    1777         152 :     m_oExtents.MaxY = dfMaxYIn;
    1778         152 : }
    1779             : 
    1780             : /************************************************************************/
    1781             : /*                            SetWGS84Extents()                         */
    1782             : /************************************************************************/
    1783             : 
    1784         152 : void OGRWFSLayer::SetWGS84Extents(double dfMinXIn, double dfMinYIn,
    1785             :                                   double dfMaxXIn, double dfMaxYIn)
    1786             : {
    1787         152 :     m_oWGS84Extents.MinX = dfMinXIn;
    1788         152 :     m_oWGS84Extents.MinY = dfMinYIn;
    1789         152 :     m_oWGS84Extents.MaxX = dfMaxXIn;
    1790         152 :     m_oWGS84Extents.MaxY = dfMaxYIn;
    1791         152 : }
    1792             : 
    1793             : /************************************************************************/
    1794             : /*                             IGetExtent()                             */
    1795             : /************************************************************************/
    1796             : 
    1797          16 : OGRErr OGRWFSLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
    1798             :                                bool bForce)
    1799             : {
    1800          16 :     if (m_oExtents.IsInit())
    1801             :     {
    1802           9 :         *psExtent = m_oExtents;
    1803           9 :         return OGRERR_NONE;
    1804             :     }
    1805             : 
    1806             :     /* If we have not yet the base layer, try to read one */
    1807             :     /* feature, and then query again OLCFastGetExtent on the */
    1808             :     /* base layer. In case the WFS response would contain the */
    1809             :     /* global extent */
    1810           7 :     if (poBaseLayer == nullptr)
    1811             :     {
    1812           7 :         ResetReading();
    1813           7 :         OGRFeature *poFeature = GetNextFeature();
    1814           7 :         delete poFeature;
    1815           7 :         ResetReading();
    1816             :     }
    1817             : 
    1818           7 :     if (TestCapability(OLCFastGetExtent))
    1819           0 :         return poBaseLayer->GetExtent(iGeomField, psExtent, bForce);
    1820             : 
    1821             :     /* In some cases, we can evaluate the result of GetFeatureCount() */
    1822             :     /* and GetExtent() with the same data */
    1823           7 :     if (CanRunGetFeatureCountAndGetExtentTogether())
    1824             :     {
    1825           7 :         bCountFeaturesInGetNextFeature = true;
    1826           7 :         nFeatures = 0;
    1827             :     }
    1828             : 
    1829           7 :     OGRErr eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
    1830             : 
    1831           7 :     if (bCountFeaturesInGetNextFeature)
    1832             :     {
    1833           7 :         if (eErr == OGRERR_NONE)
    1834             :         {
    1835           2 :             m_oExtents = *psExtent;
    1836             :         }
    1837             :         else
    1838             :         {
    1839           5 :             nFeatures = -1;
    1840             :         }
    1841           7 :         bCountFeaturesInGetNextFeature = false;
    1842             :     }
    1843             : 
    1844           7 :     return eErr;
    1845             : }
    1846             : 
    1847             : /************************************************************************/
    1848             : /*                          GetShortName()                              */
    1849             : /************************************************************************/
    1850             : 
    1851         718 : const char *OGRWFSLayer::GetShortName()
    1852             : {
    1853         718 :     const char *pszShortName = strchr(pszName, ':');
    1854         718 :     if (pszShortName == nullptr)
    1855         622 :         pszShortName = pszName;
    1856             :     else
    1857          96 :         pszShortName++;
    1858         718 :     return pszShortName;
    1859             : }
    1860             : 
    1861             : /************************************************************************/
    1862             : /*                          GetPostHeader()                             */
    1863             : /************************************************************************/
    1864             : 
    1865          62 : CPLString OGRWFSLayer::GetPostHeader()
    1866             : {
    1867          62 :     CPLString osPost;
    1868          62 :     osPost += "<?xml version=\"1.0\"?>\n";
    1869          62 :     osPost += "<wfs:Transaction xmlns:wfs=\"http://www.opengis.net/wfs\"\n";
    1870             :     osPost += "                 "
    1871          62 :               "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
    1872          62 :     osPost += "                 service=\"WFS\" version=\"";
    1873          62 :     osPost += poDS->GetVersion();
    1874          62 :     osPost += "\"\n";
    1875          62 :     osPost += "                 xmlns:gml=\"http://www.opengis.net/gml\"\n";
    1876          62 :     osPost += "                 xmlns:ogc=\"http://www.opengis.net/ogc\"\n";
    1877             :     osPost +=
    1878             :         "                 xsi:schemaLocation=\"http://www.opengis.net/wfs "
    1879          62 :         "http://schemas.opengis.net/wfs/";
    1880          62 :     osPost += poDS->GetVersion();
    1881          62 :     osPost += "/wfs.xsd ";
    1882          62 :     osPost += osTargetNamespace;
    1883          62 :     osPost += " ";
    1884             : 
    1885             :     char *pszXMLEncoded =
    1886          62 :         CPLEscapeString(GetDescribeFeatureTypeURL(FALSE), -1, CPLES_XML);
    1887          62 :     osPost += pszXMLEncoded;
    1888          62 :     CPLFree(pszXMLEncoded);
    1889             : 
    1890          62 :     osPost += "\">\n";
    1891             : 
    1892          62 :     return osPost;
    1893             : }
    1894             : 
    1895             : /************************************************************************/
    1896             : /*                          ICreateFeature()                             */
    1897             : /************************************************************************/
    1898             : 
    1899          38 : OGRErr OGRWFSLayer::ICreateFeature(OGRFeature *poFeature)
    1900             : {
    1901          38 :     if (!TestCapability(OLCSequentialWrite))
    1902             :     {
    1903           0 :         if (!poDS->SupportTransactions())
    1904           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1905             :                      "CreateFeature() not supported: no WMS-T features "
    1906             :                      "advertized by server");
    1907           0 :         else if (!poDS->UpdateMode())
    1908           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1909             :                      "CreateFeature() not supported: datasource opened as "
    1910             :                      "read-only");
    1911           0 :         return OGRERR_FAILURE;
    1912             :     }
    1913             : 
    1914          38 :     if (poGMLFeatureClass == nullptr)
    1915             :     {
    1916           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1917             :                  "Cannot insert feature because we didn't manage to parse the "
    1918             :                  ".XSD schema");
    1919           0 :         return OGRERR_FAILURE;
    1920             :     }
    1921             : 
    1922          38 :     if (poFeatureDefn->GetFieldIndex("gml_id") != 0)
    1923             :     {
    1924           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find gml_id field");
    1925           0 :         return OGRERR_FAILURE;
    1926             :     }
    1927             : 
    1928          38 :     if (poFeature->IsFieldSetAndNotNull(0))
    1929             :     {
    1930           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1931             :                  "Cannot insert a feature when gml_id field is already set");
    1932           2 :         return OGRERR_FAILURE;
    1933             :     }
    1934             : 
    1935          72 :     CPLString osPost;
    1936             : 
    1937          36 :     const char *pszShortName = GetShortName();
    1938             : 
    1939          36 :     if (!bInTransaction)
    1940             :     {
    1941          20 :         osPost += GetPostHeader();
    1942          20 :         osPost += "  <wfs:Insert>\n";
    1943             :     }
    1944          36 :     osPost += "    <feature:";
    1945          36 :     osPost += pszShortName;
    1946          36 :     osPost += " xmlns:feature=\"";
    1947          36 :     osPost += osTargetNamespace;
    1948          36 :     osPost += "\">\n";
    1949             : 
    1950         288 :     for (int i = 1; i <= poFeature->GetFieldCount(); i++)
    1951             :     {
    1952         576 :         if (poGMLFeatureClass->GetGeometryPropertyCount() == 1 &&
    1953         288 :             poGMLFeatureClass->GetGeometryProperty(0)->GetAttributeIndex() ==
    1954         288 :                 i - 1)
    1955             :         {
    1956          36 :             OGRGeometry *poGeom = poFeature->GetGeometryRef();
    1957          36 :             if (poGeom != nullptr && !osGeometryColumnName.empty())
    1958             :             {
    1959           2 :                 if (poGeom->getSpatialReference() == nullptr)
    1960           2 :                     poGeom->assignSpatialReference(m_poSRS);
    1961           2 :                 char *pszGML = nullptr;
    1962           2 :                 if (strcmp(poDS->GetVersion(), "1.1.0") == 0 ||
    1963           0 :                     atoi(poDS->GetVersion()) >= 2)
    1964             :                 {
    1965           2 :                     char **papszOptions = CSLAddString(nullptr, "FORMAT=GML3");
    1966             :                     pszGML =
    1967           2 :                         OGR_G_ExportToGMLEx((OGRGeometryH)poGeom, papszOptions);
    1968           2 :                     CSLDestroy(papszOptions);
    1969             :                 }
    1970             :                 else
    1971           0 :                     pszGML = OGR_G_ExportToGML((OGRGeometryH)poGeom);
    1972           2 :                 osPost += "      <feature:";
    1973           2 :                 osPost += osGeometryColumnName;
    1974           2 :                 osPost += ">";
    1975           2 :                 osPost += pszGML;
    1976           2 :                 osPost += "</feature:";
    1977           2 :                 osPost += osGeometryColumnName;
    1978           2 :                 osPost += ">\n";
    1979           2 :                 CPLFree(pszGML);
    1980             :             }
    1981             :         }
    1982         288 :         if (i == poFeature->GetFieldCount())
    1983          36 :             break;
    1984             : 
    1985             : #ifdef notdef
    1986             :         if (poFeature->IsFieldNull(i))
    1987             :         {
    1988             :             OGRFieldDefn *poFDefn = poFeature->GetFieldDefnRef(i);
    1989             :             osPost += "      <feature:";
    1990             :             osPost += poFDefn->GetNameRef();
    1991             :             osPost += " xsi:nil=\"true\" />\n";
    1992             :         }
    1993             :         else
    1994             : #endif
    1995         252 :             if (poFeature->IsFieldSet(i) && !poFeature->IsFieldNull(i))
    1996             :         {
    1997           6 :             OGRFieldDefn *poFDefn = poFeature->GetFieldDefnRef(i);
    1998           6 :             osPost += "      <feature:";
    1999           6 :             osPost += poFDefn->GetNameRef();
    2000           6 :             osPost += ">";
    2001           6 :             if (poFDefn->GetType() == OFTInteger)
    2002           2 :                 osPost += CPLSPrintf("%d", poFeature->GetFieldAsInteger(i));
    2003           4 :             else if (poFDefn->GetType() == OFTInteger64)
    2004             :                 osPost +=
    2005           0 :                     CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFieldAsInteger64(i));
    2006           4 :             else if (poFDefn->GetType() == OFTReal)
    2007           2 :                 osPost += CPLSPrintf("%.16g", poFeature->GetFieldAsDouble(i));
    2008             :             else
    2009             :             {
    2010           2 :                 char *pszXMLEncoded = CPLEscapeString(
    2011             :                     poFeature->GetFieldAsString(i), -1, CPLES_XML);
    2012           2 :                 osPost += pszXMLEncoded;
    2013           2 :                 CPLFree(pszXMLEncoded);
    2014             :             }
    2015           6 :             osPost += "</feature:";
    2016           6 :             osPost += poFDefn->GetNameRef();
    2017           6 :             osPost += ">\n";
    2018             :         }
    2019             :     }
    2020             : 
    2021          36 :     osPost += "    </feature:";
    2022          36 :     osPost += pszShortName;
    2023          36 :     osPost += ">\n";
    2024             : 
    2025          36 :     if (!bInTransaction)
    2026             :     {
    2027          20 :         osPost += "  </wfs:Insert>\n";
    2028          20 :         osPost += "</wfs:Transaction>\n";
    2029             :     }
    2030             :     else
    2031             :     {
    2032          16 :         osGlobalInsert += osPost;
    2033          16 :         nExpectedInserts++;
    2034          16 :         return OGRERR_NONE;
    2035             :     }
    2036             : 
    2037          20 :     CPLDebug("WFS", "Post : %s", osPost.c_str());
    2038             : 
    2039          20 :     char **papszOptions = nullptr;
    2040          20 :     papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
    2041             :     papszOptions =
    2042          20 :         CSLAddNameValue(papszOptions, "HEADERS",
    2043             :                         "Content-Type: application/xml; charset=UTF-8");
    2044             : 
    2045             :     CPLHTTPResult *psResult =
    2046          20 :         poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions);
    2047          20 :     CSLDestroy(papszOptions);
    2048             : 
    2049          20 :     if (psResult == nullptr)
    2050             :     {
    2051           6 :         return OGRERR_FAILURE;
    2052             :     }
    2053             : 
    2054          14 :     if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
    2055          12 :             nullptr ||
    2056          12 :         strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
    2057             :             nullptr)
    2058             :     {
    2059           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
    2060             :                  psResult->pabyData);
    2061           2 :         CPLHTTPDestroyResult(psResult);
    2062           2 :         return OGRERR_FAILURE;
    2063             :     }
    2064             : 
    2065          12 :     CPLDebug("WFS", "Response: %s", psResult->pabyData);
    2066             : 
    2067          12 :     CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
    2068          12 :     if (psXML == nullptr)
    2069             :     {
    2070           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
    2071             :                  psResult->pabyData);
    2072           2 :         CPLHTTPDestroyResult(psResult);
    2073           2 :         return OGRERR_FAILURE;
    2074             :     }
    2075             : 
    2076          10 :     CPLStripXMLNamespace(psXML, nullptr, TRUE);
    2077          10 :     bool bUse100Schema = false;
    2078          10 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TransactionResponse");
    2079          10 :     if (psRoot == nullptr)
    2080             :     {
    2081           2 :         psRoot = CPLGetXMLNode(psXML, "=WFS_TransactionResponse");
    2082           2 :         if (psRoot)
    2083           0 :             bUse100Schema = true;
    2084             :     }
    2085             : 
    2086          10 :     if (psRoot == nullptr)
    2087             :     {
    2088           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2089             :                  "Cannot find <TransactionResponse>");
    2090           2 :         CPLDestroyXMLNode(psXML);
    2091           2 :         CPLHTTPDestroyResult(psResult);
    2092           2 :         return OGRERR_FAILURE;
    2093             :     }
    2094             : 
    2095           8 :     CPLXMLNode *psFeatureID = nullptr;
    2096             : 
    2097           8 :     if (bUse100Schema)
    2098             :     {
    2099           0 :         if (CPLGetXMLNode(psRoot, "TransactionResult.Status.FAILED"))
    2100             :         {
    2101           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Insert failed : %s",
    2102             :                      psResult->pabyData);
    2103           0 :             CPLDestroyXMLNode(psXML);
    2104           0 :             CPLHTTPDestroyResult(psResult);
    2105           0 :             return OGRERR_FAILURE;
    2106             :         }
    2107             : 
    2108           0 :         psFeatureID = CPLGetXMLNode(psRoot, "InsertResult.FeatureId");
    2109           0 :         if (psFeatureID == nullptr)
    2110             :         {
    2111           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2112             :                      "Cannot find InsertResult.FeatureId");
    2113           0 :             CPLDestroyXMLNode(psXML);
    2114           0 :             CPLHTTPDestroyResult(psResult);
    2115           0 :             return OGRERR_FAILURE;
    2116             :         }
    2117             :     }
    2118             :     else
    2119             :     {
    2120           8 :         const char *pszFeatureIdElt = atoi(poDS->GetVersion()) >= 2
    2121           8 :                                           ? "InsertResults.Feature.ResourceId"
    2122           8 :                                           : "InsertResults.Feature.FeatureId";
    2123           8 :         psFeatureID = CPLGetXMLNode(psRoot, pszFeatureIdElt);
    2124           8 :         if (psFeatureID == nullptr)
    2125             :         {
    2126           2 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
    2127             :                      pszFeatureIdElt);
    2128           2 :             CPLDestroyXMLNode(psXML);
    2129           2 :             CPLHTTPDestroyResult(psResult);
    2130           2 :             return OGRERR_FAILURE;
    2131             :         }
    2132             :     }
    2133             : 
    2134           6 :     const char *pszFIDAttr = atoi(poDS->GetVersion()) >= 2 ? "rid" : "fid";
    2135           6 :     const char *pszFID = CPLGetXMLValue(psFeatureID, pszFIDAttr, nullptr);
    2136           6 :     if (pszFID == nullptr)
    2137             :     {
    2138           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", pszFIDAttr);
    2139           2 :         CPLDestroyXMLNode(psXML);
    2140           2 :         CPLHTTPDestroyResult(psResult);
    2141           2 :         return OGRERR_FAILURE;
    2142             :     }
    2143             : 
    2144           4 :     poFeature->SetField("gml_id", pszFID);
    2145             : 
    2146             :     /* If the returned fid is of the form layer_name.num, then use */
    2147             :     /* num as the OGR FID */
    2148           4 :     if (strncmp(pszFID, pszShortName, strlen(pszShortName)) == 0 &&
    2149           4 :         pszFID[strlen(pszShortName)] == '.')
    2150             :     {
    2151           4 :         GIntBig nFID = CPLAtoGIntBig(pszFID + strlen(pszShortName) + 1);
    2152           4 :         poFeature->SetFID(nFID);
    2153             :     }
    2154             : 
    2155           4 :     CPLDebug("WFS", "Got FID = " CPL_FRMT_GIB, poFeature->GetFID());
    2156             : 
    2157           4 :     CPLDestroyXMLNode(psXML);
    2158           4 :     CPLHTTPDestroyResult(psResult);
    2159             : 
    2160             :     /* Invalidate layer */
    2161           4 :     bReloadNeeded = true;
    2162           4 :     nFeatures = -1;
    2163           4 :     m_oExtents = OGREnvelope();
    2164             : 
    2165           4 :     return OGRERR_NONE;
    2166             : }
    2167             : 
    2168             : /************************************************************************/
    2169             : /*                             ISetFeature()                             */
    2170             : /************************************************************************/
    2171             : 
    2172          16 : OGRErr OGRWFSLayer::ISetFeature(OGRFeature *poFeature)
    2173             : {
    2174          16 :     if (!TestCapability(OLCRandomWrite))
    2175             :     {
    2176           0 :         if (!poDS->SupportTransactions())
    2177           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2178             :                      "SetFeature() not supported: no WMS-T features advertized "
    2179             :                      "by server");
    2180           0 :         else if (!poDS->UpdateMode())
    2181           0 :             CPLError(
    2182             :                 CE_Failure, CPLE_AppDefined,
    2183             :                 "SetFeature() not supported: datasource opened as read-only");
    2184           0 :         return OGRERR_FAILURE;
    2185             :     }
    2186             : 
    2187          16 :     if (poFeatureDefn->GetFieldIndex("gml_id") != 0)
    2188             :     {
    2189           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find gml_id field");
    2190           0 :         return OGRERR_FAILURE;
    2191             :     }
    2192             : 
    2193          16 :     if (poFeature->IsFieldSetAndNotNull(0) == FALSE)
    2194             :     {
    2195           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2196             :                  "Cannot update a feature when gml_id field is not set");
    2197           2 :         return OGRERR_FAILURE;
    2198             :     }
    2199             : 
    2200          14 :     if (bInTransaction)
    2201             :     {
    2202           0 :         CPLError(
    2203             :             CE_Warning, CPLE_AppDefined,
    2204             :             "SetFeature() not yet dealt in transaction. Issued immediately");
    2205             :     }
    2206             : 
    2207          14 :     const char *pszShortName = GetShortName();
    2208             : 
    2209          28 :     CPLString osPost;
    2210          14 :     osPost += GetPostHeader();
    2211             : 
    2212          14 :     osPost += "  <wfs:Update typeName=\"feature:";
    2213          14 :     osPost += pszShortName;
    2214          14 :     osPost += "\" xmlns:feature=\"";
    2215          14 :     osPost += osTargetNamespace;
    2216          14 :     osPost += "\">\n";
    2217             : 
    2218          14 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
    2219          14 :     if (!osGeometryColumnName.empty())
    2220             :     {
    2221          14 :         osPost += "    <wfs:Property>\n";
    2222          14 :         osPost += "      <wfs:Name>";
    2223          14 :         osPost += osGeometryColumnName;
    2224          14 :         osPost += "</wfs:Name>\n";
    2225          14 :         if (poGeom != nullptr)
    2226             :         {
    2227           2 :             if (poGeom->getSpatialReference() == nullptr)
    2228           2 :                 poGeom->assignSpatialReference(m_poSRS);
    2229           2 :             char *pszGML = nullptr;
    2230           2 :             if (strcmp(poDS->GetVersion(), "1.1.0") == 0 ||
    2231           0 :                 atoi(poDS->GetVersion()) >= 2)
    2232             :             {
    2233           2 :                 char **papszOptions = CSLAddString(nullptr, "FORMAT=GML3");
    2234             :                 pszGML =
    2235           2 :                     OGR_G_ExportToGMLEx((OGRGeometryH)poGeom, papszOptions);
    2236           2 :                 CSLDestroy(papszOptions);
    2237             :             }
    2238             :             else
    2239           0 :                 pszGML = OGR_G_ExportToGML((OGRGeometryH)poGeom);
    2240           2 :             osPost += "      <wfs:Value>";
    2241           2 :             osPost += pszGML;
    2242           2 :             osPost += "</wfs:Value>\n";
    2243           2 :             CPLFree(pszGML);
    2244             :         }
    2245          14 :         osPost += "    </wfs:Property>\n";
    2246             :     }
    2247             : 
    2248         112 :     for (int i = 1; i < poFeature->GetFieldCount(); i++)
    2249             :     {
    2250          98 :         OGRFieldDefn *poFDefn = poFeature->GetFieldDefnRef(i);
    2251             : 
    2252          98 :         osPost += "    <wfs:Property>\n";
    2253          98 :         osPost += "      <wfs:Name>";
    2254          98 :         osPost += poFDefn->GetNameRef();
    2255          98 :         osPost += "</wfs:Name>\n";
    2256          98 :         if (poFeature->IsFieldSetAndNotNull(i))
    2257             :         {
    2258           6 :             osPost += "      <wfs:Value>";
    2259           6 :             if (poFDefn->GetType() == OFTInteger)
    2260           2 :                 osPost += CPLSPrintf("%d", poFeature->GetFieldAsInteger(i));
    2261           4 :             else if (poFDefn->GetType() == OFTInteger64)
    2262             :                 osPost +=
    2263           0 :                     CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFieldAsInteger64(i));
    2264           4 :             else if (poFDefn->GetType() == OFTReal)
    2265           2 :                 osPost += CPLSPrintf("%.16g", poFeature->GetFieldAsDouble(i));
    2266             :             else
    2267             :             {
    2268           2 :                 char *pszXMLEncoded = CPLEscapeString(
    2269             :                     poFeature->GetFieldAsString(i), -1, CPLES_XML);
    2270           2 :                 osPost += pszXMLEncoded;
    2271           2 :                 CPLFree(pszXMLEncoded);
    2272             :             }
    2273           6 :             osPost += "</wfs:Value>\n";
    2274             :         }
    2275          98 :         osPost += "    </wfs:Property>\n";
    2276             :     }
    2277          14 :     osPost += "    <ogc:Filter>\n";
    2278          14 :     if (poDS->UseFeatureId() || bUseFeatureIdAtLayerLevel)
    2279           0 :         osPost += "      <ogc:FeatureId fid=\"";
    2280          14 :     else if (atoi(poDS->GetVersion()) >= 2)
    2281           0 :         osPost += "      <ogc:ResourceId rid=\"";
    2282             :     else
    2283          14 :         osPost += "      <ogc:GmlObjectId gml:id=\"";
    2284          14 :     osPost += poFeature->GetFieldAsString(0);
    2285          14 :     osPost += "\"/>\n";
    2286          14 :     osPost += "    </ogc:Filter>\n";
    2287          14 :     osPost += "  </wfs:Update>\n";
    2288          14 :     osPost += "</wfs:Transaction>\n";
    2289             : 
    2290          14 :     CPLDebug("WFS", "Post : %s", osPost.c_str());
    2291             : 
    2292          14 :     char **papszOptions = nullptr;
    2293          14 :     papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
    2294             :     papszOptions =
    2295          14 :         CSLAddNameValue(papszOptions, "HEADERS",
    2296             :                         "Content-Type: application/xml; charset=UTF-8");
    2297             : 
    2298             :     CPLHTTPResult *psResult =
    2299          14 :         poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions);
    2300          14 :     CSLDestroy(papszOptions);
    2301             : 
    2302          14 :     if (psResult == nullptr)
    2303             :     {
    2304           4 :         return OGRERR_FAILURE;
    2305             :     }
    2306             : 
    2307          10 :     if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
    2308           8 :             nullptr ||
    2309           8 :         strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
    2310             :             nullptr)
    2311             :     {
    2312           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
    2313             :                  psResult->pabyData);
    2314           2 :         CPLHTTPDestroyResult(psResult);
    2315           2 :         return OGRERR_FAILURE;
    2316             :     }
    2317             : 
    2318           8 :     CPLDebug("WFS", "Response: %s", psResult->pabyData);
    2319             : 
    2320           8 :     CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
    2321           8 :     if (psXML == nullptr)
    2322             :     {
    2323           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
    2324             :                  psResult->pabyData);
    2325           2 :         CPLHTTPDestroyResult(psResult);
    2326           2 :         return OGRERR_FAILURE;
    2327             :     }
    2328             : 
    2329           6 :     CPLStripXMLNamespace(psXML, nullptr, TRUE);
    2330           6 :     int bUse100Schema = false;
    2331           6 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TransactionResponse");
    2332           6 :     if (psRoot == nullptr)
    2333             :     {
    2334           2 :         psRoot = CPLGetXMLNode(psXML, "=WFS_TransactionResponse");
    2335           2 :         if (psRoot)
    2336           0 :             bUse100Schema = true;
    2337             :     }
    2338           6 :     if (psRoot == nullptr)
    2339             :     {
    2340           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2341             :                  "Cannot find <TransactionResponse>");
    2342           2 :         CPLDestroyXMLNode(psXML);
    2343           2 :         CPLHTTPDestroyResult(psResult);
    2344           2 :         return OGRERR_FAILURE;
    2345             :     }
    2346             : 
    2347           4 :     if (bUse100Schema)
    2348             :     {
    2349           0 :         if (CPLGetXMLNode(psRoot, "TransactionResult.Status.FAILED"))
    2350             :         {
    2351           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Update failed : %s",
    2352             :                      psResult->pabyData);
    2353           0 :             CPLDestroyXMLNode(psXML);
    2354           0 :             CPLHTTPDestroyResult(psResult);
    2355           0 :             return OGRERR_FAILURE;
    2356             :         }
    2357             :     }
    2358             : 
    2359           4 :     CPLDestroyXMLNode(psXML);
    2360           4 :     CPLHTTPDestroyResult(psResult);
    2361             : 
    2362             :     /* Invalidate layer */
    2363           4 :     bReloadNeeded = true;
    2364           4 :     nFeatures = -1;
    2365           4 :     m_oExtents = OGREnvelope();
    2366             : 
    2367           4 :     return OGRERR_NONE;
    2368             : }
    2369             : 
    2370             : /************************************************************************/
    2371             : /*                               GetFeature()                           */
    2372             : /************************************************************************/
    2373             : 
    2374          14 : OGRFeature *OGRWFSLayer::GetFeature(GIntBig nFID)
    2375             : {
    2376          14 :     GetLayerDefn();
    2377          14 :     if (poBaseLayer == nullptr && poFeatureDefn->GetFieldIndex("gml_id") == 0)
    2378             :     {
    2379             :         /* This is lovely hackish. We assume that then gml_id will be */
    2380             :         /* layer_name.number. This is actually what we can observe with */
    2381             :         /* GeoServer and TinyOWS */
    2382             :         CPLString osVal =
    2383          14 :             CPLSPrintf("gml_id = '%s." CPL_FRMT_GIB "'", GetShortName(), nFID);
    2384          14 :         CPLString osOldSQLWhere(osSQLWhere);
    2385          14 :         SetAttributeFilter(osVal);
    2386          14 :         OGRFeature *poFeature = GetNextFeature();
    2387             :         const char *pszOldFilter =
    2388          14 :             osOldSQLWhere.size() ? osOldSQLWhere.c_str() : nullptr;
    2389          14 :         SetAttributeFilter(pszOldFilter);
    2390          14 :         if (poFeature)
    2391          12 :             return poFeature;
    2392             :     }
    2393             : 
    2394           2 :     return OGRLayer::GetFeature(nFID);
    2395             : }
    2396             : 
    2397             : /************************************************************************/
    2398             : /*                         DeleteFromFilter()                           */
    2399             : /************************************************************************/
    2400             : 
    2401          12 : OGRErr OGRWFSLayer::DeleteFromFilter(const std::string &osOGCFilter)
    2402             : {
    2403          12 :     if (!TestCapability(OLCDeleteFeature))
    2404             :     {
    2405           0 :         if (!poDS->SupportTransactions())
    2406           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2407             :                      "DeleteFromFilter() not supported: no WMS-T features "
    2408             :                      "advertized by server");
    2409           0 :         else if (!poDS->UpdateMode())
    2410           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2411             :                      "DeleteFromFilter() not supported: datasource opened as "
    2412             :                      "read-only");
    2413           0 :         return OGRERR_FAILURE;
    2414             :     }
    2415             : 
    2416          12 :     if (poFeatureDefn->GetFieldIndex("gml_id") != 0)
    2417             :     {
    2418           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find gml_id field");
    2419           0 :         return OGRERR_FAILURE;
    2420             :     }
    2421          12 :     const char *pszShortName = GetShortName();
    2422             : 
    2423          24 :     CPLString osPost;
    2424          12 :     osPost += GetPostHeader();
    2425             : 
    2426          12 :     osPost += "  <wfs:Delete xmlns:feature=\"";
    2427          12 :     osPost += osTargetNamespace;
    2428          12 :     osPost += "\" typeName=\"feature:";
    2429          12 :     osPost += pszShortName;
    2430          12 :     osPost += "\">\n";
    2431          12 :     osPost += "    <ogc:Filter>\n";
    2432          12 :     osPost += osOGCFilter;
    2433          12 :     osPost += "    </ogc:Filter>\n";
    2434          12 :     osPost += "  </wfs:Delete>\n";
    2435          12 :     osPost += "</wfs:Transaction>\n";
    2436             : 
    2437          12 :     CPLDebug("WFS", "Post : %s", osPost.c_str());
    2438             : 
    2439          12 :     char **papszOptions = nullptr;
    2440          12 :     papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
    2441             :     papszOptions =
    2442          12 :         CSLAddNameValue(papszOptions, "HEADERS",
    2443             :                         "Content-Type: application/xml; charset=UTF-8");
    2444             : 
    2445             :     CPLHTTPResult *psResult =
    2446          12 :         poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions);
    2447          12 :     CSLDestroy(papszOptions);
    2448             : 
    2449          12 :     if (psResult == nullptr)
    2450             :     {
    2451           4 :         return OGRERR_FAILURE;
    2452             :     }
    2453             : 
    2454           8 :     if (strstr((const char *)psResult->pabyData, "<ServiceExceptionReport") !=
    2455           8 :             nullptr ||
    2456           8 :         strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
    2457             :             nullptr)
    2458             :     {
    2459           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
    2460             :                  psResult->pabyData);
    2461           0 :         CPLHTTPDestroyResult(psResult);
    2462           0 :         return OGRERR_FAILURE;
    2463             :     }
    2464             : 
    2465           8 :     CPLDebug("WFS", "Response: %s", psResult->pabyData);
    2466             : 
    2467           8 :     CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
    2468           8 :     if (psXML == nullptr)
    2469             :     {
    2470           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
    2471             :                  psResult->pabyData);
    2472           2 :         CPLHTTPDestroyResult(psResult);
    2473           2 :         return OGRERR_FAILURE;
    2474             :     }
    2475             : 
    2476           6 :     CPLStripXMLNamespace(psXML, nullptr, TRUE);
    2477           6 :     bool bUse100Schema = false;
    2478           6 :     CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TransactionResponse");
    2479           6 :     if (psRoot == nullptr)
    2480             :     {
    2481           2 :         psRoot = CPLGetXMLNode(psXML, "=WFS_TransactionResponse");
    2482           2 :         if (psRoot)
    2483           0 :             bUse100Schema = true;
    2484             :     }
    2485           6 :     if (psRoot == nullptr)
    2486             :     {
    2487           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2488             :                  "Cannot find <TransactionResponse>");
    2489           2 :         CPLDestroyXMLNode(psXML);
    2490           2 :         CPLHTTPDestroyResult(psResult);
    2491           2 :         return OGRERR_FAILURE;
    2492             :     }
    2493             : 
    2494           4 :     if (bUse100Schema)
    2495             :     {
    2496           0 :         if (CPLGetXMLNode(psRoot, "TransactionResult.Status.FAILED"))
    2497             :         {
    2498           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Delete failed : %s",
    2499             :                      psResult->pabyData);
    2500           0 :             CPLDestroyXMLNode(psXML);
    2501           0 :             CPLHTTPDestroyResult(psResult);
    2502           0 :             return OGRERR_FAILURE;
    2503             :         }
    2504             :     }
    2505             : 
    2506           4 :     CPLDestroyXMLNode(psXML);
    2507           4 :     CPLHTTPDestroyResult(psResult);
    2508             : 
    2509             :     /* Invalidate layer */
    2510           4 :     bReloadNeeded = true;
    2511           4 :     nFeatures = -1;
    2512           4 :     m_oExtents = OGREnvelope();
    2513             : 
    2514           4 :     return OGRERR_NONE;
    2515             : }
    2516             : 
    2517             : /************************************************************************/
    2518             : /*                            DeleteFeature()                           */
    2519             : /************************************************************************/
    2520             : 
    2521          12 : OGRErr OGRWFSLayer::DeleteFeature(GIntBig nFID)
    2522             : {
    2523          12 :     if (!TestCapability(OLCDeleteFeature))
    2524             :     {
    2525           0 :         if (!poDS->SupportTransactions())
    2526           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2527             :                      "DeleteFeature() not supported: no WMS-T features "
    2528             :                      "advertized by server");
    2529           0 :         else if (!poDS->UpdateMode())
    2530           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2531             :                      "DeleteFeature() not supported: datasource opened as "
    2532             :                      "read-only");
    2533           0 :         return OGRERR_FAILURE;
    2534             :     }
    2535             : 
    2536          12 :     if (poFeatureDefn->GetFieldIndex("gml_id") != 0)
    2537             :     {
    2538           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find gml_id field");
    2539           0 :         return OGRERR_FAILURE;
    2540             :     }
    2541             : 
    2542          12 :     OGRFeature *poFeature = GetFeature(nFID);
    2543          12 :     if (poFeature == nullptr)
    2544             :     {
    2545           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2546             :                  "Cannot find feature " CPL_FRMT_GIB, nFID);
    2547           2 :         return OGRERR_FAILURE;
    2548             :     }
    2549             : 
    2550          10 :     const char *pszGMLID = poFeature->GetFieldAsString("gml_id");
    2551          10 :     if (pszGMLID == nullptr)
    2552             :     {
    2553           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2554             :                  "Cannot delete a feature with gml_id unset");
    2555           0 :         delete poFeature;
    2556           0 :         return OGRERR_FAILURE;
    2557             :     }
    2558             : 
    2559          10 :     if (bInTransaction)
    2560             :     {
    2561           0 :         CPLError(
    2562             :             CE_Warning, CPLE_AppDefined,
    2563             :             "DeleteFeature() not yet dealt in transaction. Issued immediately");
    2564             :     }
    2565             : 
    2566          20 :     CPLString osGMLID = pszGMLID;
    2567          10 :     pszGMLID = nullptr;
    2568          10 :     delete poFeature;
    2569          10 :     poFeature = nullptr;
    2570             : 
    2571          20 :     CPLString osFilter;
    2572          10 :     osFilter = "<ogc:FeatureId fid=\"";
    2573          10 :     osFilter += osGMLID;
    2574          10 :     osFilter += "\"/>\n";
    2575          10 :     return DeleteFromFilter(osFilter);
    2576             : }
    2577             : 
    2578             : /************************************************************************/
    2579             : /*                         StartTransaction()                           */
    2580             : /************************************************************************/
    2581             : 
    2582          24 : OGRErr OGRWFSLayer::StartTransaction()
    2583             : {
    2584          24 :     if (!TestCapability(OLCTransactions))
    2585             :     {
    2586           0 :         if (!poDS->SupportTransactions())
    2587           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2588             :                      "StartTransaction() not supported: no WMS-T features "
    2589             :                      "advertized by server");
    2590           0 :         else if (!poDS->UpdateMode())
    2591           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2592             :                      "StartTransaction() not supported: datasource opened as "
    2593             :                      "read-only");
    2594           0 :         return OGRERR_FAILURE;
    2595             :     }
    2596             : 
    2597          24 :     if (bInTransaction)
    2598             :     {
    2599           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2600             :                  "StartTransaction() has already been called");
    2601           2 :         return OGRERR_FAILURE;
    2602             :     }
    2603             : 
    2604          22 :     bInTransaction = true;
    2605          22 :     osGlobalInsert = "";
    2606          22 :     nExpectedInserts = 0;
    2607          22 :     aosFIDList.resize(0);
    2608             : 
    2609          22 :     return OGRERR_NONE;
    2610             : }
    2611             : 
    2612             : /************************************************************************/
    2613             : /*                        CommitTransaction()                           */
    2614             : /************************************************************************/
    2615             : 
    2616          20 : OGRErr OGRWFSLayer::CommitTransaction()
    2617             : {
    2618          20 :     if (!TestCapability(OLCTransactions))
    2619             :     {
    2620           0 :         if (!poDS->SupportTransactions())
    2621           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2622             :                      "CommitTransaction() not supported: no WMS-T features "
    2623             :                      "advertized by server");
    2624           0 :         else if (!poDS->UpdateMode())
    2625           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2626             :                      "CommitTransaction() not supported: datasource opened as "
    2627             :                      "read-only");
    2628           0 :         return OGRERR_FAILURE;
    2629             :     }
    2630             : 
    2631          20 :     if (!bInTransaction)
    2632             :     {
    2633           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2634             :                  "StartTransaction() has not yet been called");
    2635           2 :         return OGRERR_FAILURE;
    2636             :     }
    2637             : 
    2638          18 :     if (!osGlobalInsert.empty())
    2639             :     {
    2640          16 :         CPLString osPost = GetPostHeader();
    2641          16 :         osPost += "  <wfs:Insert>\n";
    2642          16 :         osPost += osGlobalInsert;
    2643          16 :         osPost += "  </wfs:Insert>\n";
    2644          16 :         osPost += "</wfs:Transaction>\n";
    2645             : 
    2646          16 :         bInTransaction = false;
    2647          16 :         osGlobalInsert = "";
    2648          16 :         int l_nExpectedInserts = nExpectedInserts;
    2649          16 :         nExpectedInserts = 0;
    2650             : 
    2651          16 :         CPLDebug("WFS", "Post : %s", osPost.c_str());
    2652             : 
    2653          16 :         char **papszOptions = nullptr;
    2654             :         papszOptions =
    2655          16 :             CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str());
    2656             :         papszOptions =
    2657          16 :             CSLAddNameValue(papszOptions, "HEADERS",
    2658             :                             "Content-Type: application/xml; charset=UTF-8");
    2659             : 
    2660             :         CPLHTTPResult *psResult =
    2661          16 :             poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions);
    2662          16 :         CSLDestroy(papszOptions);
    2663             : 
    2664          16 :         if (psResult == nullptr)
    2665             :         {
    2666           0 :             return OGRERR_FAILURE;
    2667             :         }
    2668             : 
    2669          16 :         if (strstr((const char *)psResult->pabyData,
    2670          14 :                    "<ServiceExceptionReport") != nullptr ||
    2671          14 :             strstr((const char *)psResult->pabyData, "<ows:ExceptionReport") !=
    2672             :                 nullptr)
    2673             :         {
    2674           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2675             :                      "Error returned by server : %s", psResult->pabyData);
    2676           2 :             CPLHTTPDestroyResult(psResult);
    2677           2 :             return OGRERR_FAILURE;
    2678             :         }
    2679             : 
    2680          14 :         CPLDebug("WFS", "Response: %s", psResult->pabyData);
    2681             : 
    2682          14 :         CPLXMLNode *psXML = CPLParseXMLString((const char *)psResult->pabyData);
    2683          14 :         if (psXML == nullptr)
    2684             :         {
    2685           2 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
    2686             :                      psResult->pabyData);
    2687           2 :             CPLHTTPDestroyResult(psResult);
    2688           2 :             return OGRERR_FAILURE;
    2689             :         }
    2690             : 
    2691          12 :         CPLStripXMLNamespace(psXML, nullptr, TRUE);
    2692          12 :         bool bUse100Schema = false;
    2693          12 :         CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=TransactionResponse");
    2694          12 :         if (psRoot == nullptr)
    2695             :         {
    2696           2 :             psRoot = CPLGetXMLNode(psXML, "=WFS_TransactionResponse");
    2697           2 :             if (psRoot)
    2698           0 :                 bUse100Schema = true;
    2699             :         }
    2700             : 
    2701          12 :         if (psRoot == nullptr)
    2702             :         {
    2703           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2704             :                      "Cannot find <TransactionResponse>");
    2705           2 :             CPLDestroyXMLNode(psXML);
    2706           2 :             CPLHTTPDestroyResult(psResult);
    2707           2 :             return OGRERR_FAILURE;
    2708             :         }
    2709             : 
    2710          10 :         if (bUse100Schema)
    2711             :         {
    2712           0 :             if (CPLGetXMLNode(psRoot, "TransactionResult.Status.FAILED"))
    2713             :             {
    2714           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Insert failed : %s",
    2715             :                          psResult->pabyData);
    2716           0 :                 CPLDestroyXMLNode(psXML);
    2717           0 :                 CPLHTTPDestroyResult(psResult);
    2718           0 :                 return OGRERR_FAILURE;
    2719             :             }
    2720             : 
    2721             :             /* TODO */
    2722             :         }
    2723             :         else
    2724             :         {
    2725          10 :             int nGotInserted = atoi(
    2726             :                 CPLGetXMLValue(psRoot, "TransactionSummary.totalInserted", ""));
    2727          10 :             if (nGotInserted != l_nExpectedInserts)
    2728             :             {
    2729           2 :                 CPLError(
    2730             :                     CE_Failure, CPLE_AppDefined,
    2731             :                     "Only %d features were inserted whereas %d where expected",
    2732             :                     nGotInserted, l_nExpectedInserts);
    2733           2 :                 CPLDestroyXMLNode(psXML);
    2734           2 :                 CPLHTTPDestroyResult(psResult);
    2735           2 :                 return OGRERR_FAILURE;
    2736             :             }
    2737             : 
    2738             :             CPLXMLNode *psInsertResults =
    2739           8 :                 CPLGetXMLNode(psRoot, "InsertResults");
    2740           8 :             if (psInsertResults == nullptr)
    2741             :             {
    2742           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2743             :                          "Cannot find node InsertResults");
    2744           2 :                 CPLDestroyXMLNode(psXML);
    2745           2 :                 CPLHTTPDestroyResult(psResult);
    2746           2 :                 return OGRERR_FAILURE;
    2747             :             }
    2748             : 
    2749           6 :             aosFIDList.resize(0);
    2750             : 
    2751           6 :             CPLXMLNode *psChild = psInsertResults->psChild;
    2752           8 :             while (psChild)
    2753             :             {
    2754             :                 const char *pszFID =
    2755           4 :                     CPLGetXMLValue(psChild, "FeatureId.fid", nullptr);
    2756           4 :                 if (pszFID == nullptr)
    2757             :                 {
    2758           2 :                     CPLError(CE_Failure, CPLE_AppDefined, "Cannot find fid");
    2759           2 :                     CPLDestroyXMLNode(psXML);
    2760           2 :                     CPLHTTPDestroyResult(psResult);
    2761           2 :                     return OGRERR_FAILURE;
    2762             :                 }
    2763           2 :                 aosFIDList.push_back(pszFID);
    2764             : 
    2765           2 :                 psChild = psChild->psNext;
    2766             :             }
    2767             : 
    2768           4 :             if ((int)aosFIDList.size() != nGotInserted)
    2769             :             {
    2770           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2771             :                          "Inconsistent InsertResults: did not get expected FID "
    2772             :                          "count");
    2773           2 :                 CPLDestroyXMLNode(psXML);
    2774           2 :                 CPLHTTPDestroyResult(psResult);
    2775           2 :                 return OGRERR_FAILURE;
    2776             :             }
    2777             :         }
    2778             : 
    2779           2 :         CPLDestroyXMLNode(psXML);
    2780           2 :         CPLHTTPDestroyResult(psResult);
    2781             :     }
    2782             : 
    2783           4 :     bInTransaction = false;
    2784           4 :     osGlobalInsert = "";
    2785           4 :     nExpectedInserts = 0;
    2786             : 
    2787           4 :     return OGRERR_NONE;
    2788             : }
    2789             : 
    2790             : /************************************************************************/
    2791             : /*                      RollbackTransaction()                           */
    2792             : /************************************************************************/
    2793             : 
    2794           6 : OGRErr OGRWFSLayer::RollbackTransaction()
    2795             : {
    2796           6 :     if (!TestCapability(OLCTransactions))
    2797             :     {
    2798           0 :         if (!poDS->SupportTransactions())
    2799           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2800             :                      "RollbackTransaction() not supported: no WMS-T features "
    2801             :                      "advertized by server");
    2802           0 :         else if (!poDS->UpdateMode())
    2803           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2804             :                      "RollbackTransaction() not supported: datasource opened "
    2805             :                      "as read-only");
    2806           0 :         return OGRERR_FAILURE;
    2807             :     }
    2808             : 
    2809           6 :     if (!bInTransaction)
    2810             :     {
    2811           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2812             :                  "StartTransaction() has not yet been called");
    2813           2 :         return OGRERR_FAILURE;
    2814             :     }
    2815             : 
    2816           4 :     bInTransaction = false;
    2817           4 :     osGlobalInsert = "";
    2818           4 :     nExpectedInserts = 0;
    2819             : 
    2820           4 :     return OGRERR_NONE;
    2821             : }
    2822             : 
    2823             : /************************************************************************/
    2824             : /*                    SetRequiredOutputFormat()                         */
    2825             : /************************************************************************/
    2826             : 
    2827           0 : void OGRWFSLayer::SetRequiredOutputFormat(const char *pszRequiredOutputFormatIn)
    2828             : {
    2829           0 :     CPLFree(pszRequiredOutputFormat);
    2830           0 :     if (pszRequiredOutputFormatIn)
    2831             :     {
    2832           0 :         pszRequiredOutputFormat = CPLStrdup(pszRequiredOutputFormatIn);
    2833             :     }
    2834             :     else
    2835             :     {
    2836           0 :         pszRequiredOutputFormat = nullptr;
    2837             :     }
    2838           0 : }
    2839             : 
    2840             : /************************************************************************/
    2841             : /*                            SetOrderBy()                              */
    2842             : /************************************************************************/
    2843             : 
    2844           0 : void OGRWFSLayer::SetOrderBy(const std::vector<OGRWFSSortDesc> &aoSortColumnsIn)
    2845             : {
    2846           0 :     aoSortColumns = aoSortColumnsIn;
    2847           0 : }

Generated by: LCOV version 1.14