1 /**********************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  WFS server implementation
6  * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7  *
8  **********************************************************************
9  * Copyright (c) 2002, Daniel Morissette, DM Solutions Group Inc
10  * Copyright (c) 2013, Even Rouault
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in
20  * all copies of this Software or works derived from this Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  ****************************************************************************/
29 
30 #include "mapserver.h"
31 #include "mapows.h"
32 
33 
34 
35 #if defined(USE_WFS_SVR)
36 
37 /* There is a dependency to GDAL/OGR for the GML driver and MiniXML parser */
38 #include "cpl_minixml.h"
39 #include "cpl_conv.h"
40 #include "cpl_string.h"
41 
42 #include "mapogcfilter.h"
43 #include "mapowscommon.h"
44 #include "maptemplate.h"
45 
46 #if defined(USE_LIBXML2)
47 #include "maplibxml2.h"
48 #endif
49 
50 static int msWFSAnalyzeStoredQuery(mapObj* map,
51                                    wfsParamsObj *wfsparams,
52                                    const char* id,
53                                    const char* pszResolvedQuery);
54 static void msWFSSimplifyPropertyNameAndFilter(wfsParamsObj *wfsparams);
55 static void msWFSAnalyzeStartIndexAndFeatureCount(mapObj *map, const wfsParamsObj *paramsObj,
56                                                   int bIsHits,
57                                                   int *pmaxfeatures, int* pstartindex);
58 static int msWFSRunBasicGetFeature(mapObj* map,
59                                    layerObj* lp,
60                                    const wfsParamsObj *paramsObj,
61                                    int nWFSVersion);
62 
63 /* Must be sorted from more recent to older one */
64 static const int wfsSupportedVersions[] = {OWS_2_0_0, OWS_1_1_0, OWS_1_0_0};
65 static const char* const wfsSupportedVersionsStr[] = { "2.0.0", "1.1.0", "1.0.0" };
66 static const int wfsNumSupportedVersions =
67     (int)(sizeof(wfsSupportedVersions)/sizeof(wfsSupportedVersions[0]));
68 static const char* const wfsUnsupportedOperations[] = { "GetFeatureWithLock",
69                                                   "LockFeature",
70                                                   "Transaction",
71                                                   "CreateStoredQuery",
72                                                   "DropStoredQuery" };
73 static const int wfsNumUnsupportedOperations =
74     (int)(sizeof(wfsUnsupportedOperations)/sizeof(wfsUnsupportedOperations[0]));
75 
76 #define WFS_LATEST_VERSION  wfsSupportedVersionsStr[0]
77 
78 /* Supported DescribeFeature formats */
79 typedef enum
80 {
81     OWS_DEFAULT_SCHEMA, /* basically a GML 2.1 schema */
82     OWS_SFE_SCHEMA, /* GML for simple feature exchange (formerly GML3L0) */
83     OWS_GML32_SFE_SCHEMA /* GML 3.2 Simple Features Level 0 */
84 } WFSSchemaVersion;
85 
86 /*
87 ** msWFSGetIndexUnsupportedOperation()
88 **
89 ** Return index of pszOp in wfsUnsupportedOperations, or -1 otherwise
90 */
91 
msWFSGetIndexUnsupportedOperation(const char * pszOp)92 static int msWFSGetIndexUnsupportedOperation(const char* pszOp)
93 {
94     int i;
95     for(i = 0; i < wfsNumUnsupportedOperations; i++ )
96     {
97         if( strcasecmp(wfsUnsupportedOperations[i], pszOp) == 0 )
98             return i;
99     }
100     return -1;
101 }
102 
103 
104 static
105 const char *msWFSGetDefaultVersion(mapObj *map);
106 
107 /*
108 ** msWFSException()
109 **
110 ** Report current MapServer error in XML exception format.
111 */
112 
113 static
msWFSExceptionInternal(mapObj * map,const char * locator,const char * code,const char * version,int locatorShouldBeNull)114 int msWFSExceptionInternal(mapObj *map, const char *locator, const char *code,
115                            const char *version, int locatorShouldBeNull )
116 {
117   char *schemalocation = NULL;
118   /* In WFS, exceptions are always XML.
119   */
120 
121   if( version == NULL )
122     version = msWFSGetDefaultVersion(map);
123 
124   if( msOWSParseVersionString(version) >= OWS_2_0_0 )
125     return msWFSException20( map, (locatorShouldBeNull) ? NULL : locator, code );
126   if( msOWSParseVersionString(version) >= OWS_1_1_0 )
127     return msWFSException11( map, (locatorShouldBeNull) ? NULL : locator, code, version );
128 
129   msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
130   msIO_sendHeaders();
131 
132   msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
133 
134   msIO_printf("<ServiceExceptionReport ");
135   msIO_printf("version=\"1.2.0\" ");
136   msIO_printf("xmlns=\"http://www.opengis.net/ogc\" ");
137   msIO_printf("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
138   schemalocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
139   msIO_printf("xsi:schemaLocation=\"http://www.opengis.net/ogc %s/wfs/1.0.0/OGC-exception.xsd\">\n", schemalocation);
140   free(schemalocation);
141   msIO_printf("  <ServiceException code=\"%s\" locator=\"%s\">\n", code, locator);
142   /* Optional <Locator> element currently unused. */
143   /* msIO_printf("    <Message>\n"); */
144   msWriteErrorXML(stdout);
145   /* msIO_printf("    </Message>\n"); */
146   msIO_printf("  </ServiceException>\n");
147   msIO_printf("</ServiceExceptionReport>\n");
148 
149   return MS_FAILURE; /* so we can call 'return msWFSException();' anywhere */
150 }
151 
msWFSException(mapObj * map,const char * locator,const char * code,const char * version)152 int msWFSException(mapObj *map, const char *locator, const char *code,
153                    const char *version )
154 {
155     return msWFSExceptionInternal(map, locator, code, version, FALSE);
156 }
157 
158 
159 /*
160 ** msWFSExceptionNoLocator()
161 **
162 ** Report current MapServer error in XML exception format.
163 ** For WFS >= 1.1.0, the locator will be ignored. It will be just used
164 ** for legacy WFS 1.0 exceptions.
165 */
166 
167 static
msWFSExceptionNoLocator(mapObj * map,const char * locator,const char * code,const char * version)168 int msWFSExceptionNoLocator(mapObj *map, const char *locator, const char *code,
169                    const char *version )
170 {
171     return msWFSExceptionInternal(map, locator, code, version, TRUE);
172 }
173 
174 /*
175 ** Helper function to build a list of output formats.
176 **
177 ** Given a layer it will return all formats valid for that layer, otherwise
178 ** all formats permitted on layers in the map are returned.  The string
179 ** returned should be freed by the caller.
180 */
181 
msWFSGetOutputFormatList(mapObj * map,layerObj * layer,int nWFSVersion)182 char *msWFSGetOutputFormatList(mapObj *map, layerObj *layer, int nWFSVersion )
183 {
184   int i, got_map_list = 0;
185   static const int out_list_size = 20000;
186   char *out_list = (char*) msSmallCalloc(1,out_list_size);
187 
188   if( nWFSVersion == OWS_1_0_0 )
189       strcpy(out_list,"GML2");
190   else if( nWFSVersion == OWS_1_1_0 )
191       strcpy(out_list,"text/xml; subtype=gml/3.1.1");
192   else
193       strcpy(out_list,"application/gml+xml; version=3.2,"
194                       "text/xml; subtype=gml/3.2.1,"
195                       "text/xml; subtype=gml/3.1.1,"
196                       "text/xml; subtype=gml/2.1.2");
197 
198   for( i = 0; i < map->numlayers; i++ ) {
199     const char *format_list;
200     layerObj *lp;
201     int j, n;
202     char **tokens;
203 
204     lp = GET_LAYER(map, i);
205     if( layer != NULL && layer != lp )
206       continue;
207 
208     format_list = msOWSLookupMetadata(&(lp->metadata),
209                                       "F","getfeature_formatlist");
210 
211     if( format_list == NULL && !got_map_list ) {
212       format_list = msOWSLookupMetadata(&(map->web.metadata),
213                                         "F","getfeature_formatlist");
214       got_map_list = 1;
215     }
216 
217     if( format_list == NULL )
218       continue;
219 
220     n = 0;
221     tokens = msStringSplit(format_list, ',', &n);
222 
223     for( j = 0; j < n; j++ ) {
224       int iformat;
225       const char *fname, *hit;
226       outputFormatObj *format_obj;
227 
228       msStringTrim( tokens[j] );
229       iformat = msGetOutputFormatIndex(map,tokens[j]);
230       if( iformat < 0 )
231         continue;
232 
233       format_obj = map->outputformatlist[iformat];
234 
235       fname = format_obj->name;
236       if( nWFSVersion >= OWS_1_1_0
237           && format_obj->mimetype != NULL )
238         fname = format_obj->mimetype;
239 
240       hit = strstr(out_list,fname);
241       if( hit != NULL
242           && (hit[strlen(fname)] == '\0' || hit[strlen(fname)] == ','))
243         continue;
244 
245       if( strlen(out_list) + strlen(fname)+3 < out_list_size ) {
246         strcat( out_list, "," );
247         strcat( out_list, fname );
248       } else
249         break;
250     }
251 
252     msFreeCharArray( tokens, n );
253   }
254 
255   return out_list;
256 }
257 
258 /*
259 **
260 */
msWFSPrintRequestCap(const char * wmtver,const char * request,const char * script_url,const char * format_tag,const char * formats_list)261 static void msWFSPrintRequestCap(const char *wmtver, const char *request,
262                                  const char *script_url,
263                                  const char *format_tag,
264                                  const char *formats_list)
265 {
266   msIO_printf("    <%s>\n", request);
267 
268   /* We expect to receive a NULL-terminated args list of formats */
269   if (format_tag != NULL) {
270     int i, n;
271     char **tokens;
272 
273     n = 0;
274     tokens = msStringSplit(formats_list, ',', &n);
275 
276     msIO_printf("      <%s>\n", format_tag);
277 
278     for( i = 0; i < n; i++ ) {
279       msIO_printf("        <%s/>\n", tokens[i] );
280     }
281 
282     msFreeCharArray( tokens, n );
283 
284     msIO_printf("      </%s>\n", format_tag);
285   }
286 
287   msIO_printf("      <DCPType>\n");
288   msIO_printf("        <HTTP>\n");
289   msIO_printf("          <Get onlineResource=\"%s\" />\n", script_url);
290   msIO_printf("        </HTTP>\n");
291   msIO_printf("      </DCPType>\n");
292   msIO_printf("      <DCPType>\n");
293   msIO_printf("        <HTTP>\n");
294   msIO_printf("          <Post onlineResource=\"%s\" />\n", script_url);
295   msIO_printf("        </HTTP>\n");
296   msIO_printf("      </DCPType>\n");
297 
298 
299   msIO_printf("    </%s>\n", request);
300 }
301 
302 
303 /* msWFSLocateSRSInList()
304 **
305 ** Utility function to check if a space separated list contains  the one passed in argument.
306 **  The list comes normaly from ows_srs metadata, and is expected to use the simple EPSG notation
307 ** (EPSG:4326 ESPG:42304 ...). The srs comes from the query string and can either
308 ** be of simple EPSG format or using gc:def:crs:EPSG:xxx format
309 */
msWFSLocateSRSInList(const char * pszList,const char * srs)310 int msWFSLocateSRSInList(const char *pszList, const char *srs)
311 {
312   int nTokens,i;
313   char **tokens = NULL;
314   int bFound = MS_FALSE;
315   char epsg_string[100];
316   const char *code;
317 
318   if (!pszList || !srs)
319     return MS_FALSE;
320 
321   if (strncasecmp(srs, "EPSG:",5) == 0)
322     code = srs+5;
323   else if (strncasecmp(srs, "urn:ogc:def:crs:EPSG:",21) == 0) {
324     if (srs[21] == ':')
325       code = srs+21;
326     else
327       code = srs+20;
328 
329     while( *code != ':' && *code != '\0')
330       code++;
331     if( *code == ':' )
332       code++;
333   } else if (strncasecmp(srs, "urn:EPSG:geographicCRS:",23) == 0)
334     code = srs + 23;
335   else
336     return MS_FALSE;
337 
338   snprintf( epsg_string, sizeof(epsg_string), "EPSG:%s", code );
339 
340   tokens = msStringSplit(pszList, ' ', &nTokens );
341   if (tokens && nTokens > 0) {
342     for (i=0; i<nTokens; i++) {
343       if (strcasecmp(tokens[i],  epsg_string) == 0) {
344         bFound = MS_TRUE;
345         break;
346       }
347     }
348   }
349   msFreeCharArray(tokens, nTokens);
350 
351   return bFound;
352 }
353 
354 /* msWFSGetFeatureApplySRS()
355 **
356 ** Utility function called from msWFSGetFeature. It is assumed that at this point
357 ** all queried layers are turned ON
358 */
msWFSGetFeatureApplySRS(mapObj * map,const char * srs,int nWFSVersion)359 static int msWFSGetFeatureApplySRS(mapObj *map, const char *srs, int nWFSVersion)
360 {
361   char *pszMapSRS=NULL;
362   char *pszOutputSRS=NULL;
363   layerObj *lp;
364   int i;
365 
366   /*validation of SRS
367     - wfs 1.0 does not have an srsname parameter so all queried layers should be advertized using the
368       same srs. For wfs 1.1.0 an srsName can be passed, we should validate that It is valid for all
369       queries layers
370   */
371 
372   /* Start by applying the default service SRS to the mapObj,
373    * make sure we reproject the map extent if a projection was
374    * already set
375    */
376   msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE, &pszMapSRS);
377   if(pszMapSRS && nWFSVersion >  OWS_1_0_0){
378     projectionObj proj;
379     msInitProjection(&proj);
380     msProjectionInheritContextFrom(&proj, &(map->projection));
381     if (map->projection.numargs > 0 && msLoadProjectionStringEPSG(&proj, pszMapSRS) == 0) {
382       msProjectRect(&(map->projection), &proj, &map->extent);
383     }
384     msLoadProjectionStringEPSG(&(map->projection), pszMapSRS);
385     msFreeProjection(&proj);
386   }
387 
388   if (srs == NULL || nWFSVersion == OWS_1_0_0) {
389     for (i=0; i<map->numlayers; i++) {
390       char *pszLayerSRS;
391       lp = GET_LAYER(map, i);
392       if (lp->status != MS_ON)
393         continue;
394 
395       if (pszMapSRS)
396         pszLayerSRS = pszMapSRS;
397       else
398         msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE, &pszLayerSRS);
399 
400       if (pszLayerSRS == NULL) {
401         msSetError(MS_WFSERR,
402                    "Server config error: SRS must be set at least at the map or at the layer level.",
403                    "msWFSGetFeature()");
404         if (pszOutputSRS)
405           msFree(pszOutputSRS);
406         /*pszMapSrs would also be NULL, no use freeing*/
407         return MS_FAILURE;
408       }
409       if (pszOutputSRS == NULL)
410         pszOutputSRS = msStrdup(pszLayerSRS);
411       else if (strcasecmp(pszLayerSRS,pszOutputSRS) != 0) {
412         msSetError(MS_WFSERR,
413                    "Invalid GetFeature Request: All TYPENAMES in a single GetFeature request must have been advertized in the same SRS.  Please check the capabilities and reformulate your request.",
414                    "msWFSGetFeature()");
415         if (pszOutputSRS)
416           msFree(pszOutputSRS);
417         if(pszLayerSRS != pszMapSRS)
418           msFree(pszLayerSRS);
419         msFree(pszMapSRS);
420         return MS_FAILURE;
421       }
422       if(pszLayerSRS != pszMapSRS)
423         msFree(pszLayerSRS);
424     }
425   } else { /*srs is given so it should be valid for all layers*/
426     /*get all the srs defined at the map level and check them aginst the srsName passed
427       as argument*/
428     msFree(pszMapSRS);
429     msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_FALSE, &pszMapSRS);
430     if (pszMapSRS) {
431       if (!msWFSLocateSRSInList(pszMapSRS, srs)) {
432         msSetError(MS_WFSERR,
433                    "Invalid GetFeature Request:Invalid SRS.  Please check the capabilities and reformulate your request.",
434                    "msWFSGetFeature()");
435         msFree(pszMapSRS);
436         return MS_FAILURE;
437       }
438       pszOutputSRS = msStrdup(srs);
439     } else {
440       for (i=0; i<map->numlayers; i++) {
441         char *pszLayerSRS=NULL;
442         lp = GET_LAYER(map, i);
443         if (lp->status != MS_ON)
444           continue;
445 
446         msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_FALSE, &pszLayerSRS);
447         if (!pszLayerSRS) {
448           msSetError(MS_WFSERR,
449                      "Server config error: SRS must be set at least at the map or at the layer level.",
450                      "msWFSGetFeature()");
451           msFree(pszMapSRS);
452           return MS_FAILURE;
453         }
454         if (!msWFSLocateSRSInList(pszLayerSRS, srs)) {
455           msSetError(MS_WFSERR,
456                      "Invalid GetFeature Request:Invalid SRS.  Please check the capabilities and reformulate your request.",
457                      "msWFSGetFeature()");
458           msFree(pszMapSRS);
459           msFree(pszLayerSRS);
460           return MS_FAILURE;
461         }
462         msFree(pszLayerSRS);
463       }
464       pszOutputSRS = msStrdup(srs);
465     }
466   }
467 
468   if (pszOutputSRS) {
469     projectionObj sProjTmp;
470     int nTmp=0;
471 
472     msInitProjection(&sProjTmp);
473     msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
474     if( nWFSVersion >= OWS_1_1_0 ) {
475       nTmp = msLoadProjectionStringEPSG(&(sProjTmp), pszOutputSRS);
476     } else {
477       nTmp = msLoadProjectionString(&(sProjTmp), pszOutputSRS);
478     }
479     if (nTmp == 0) {
480       msProjectRect(&(map->projection), &(sProjTmp), &map->extent);
481     }
482     msFreeProjection(&(sProjTmp));
483 
484     if (nTmp != 0) {
485       msSetError(MS_WFSERR, "msLoadProjectionString() failed", "msWFSGetFeature()");
486       return MS_FAILURE;
487     }
488 
489     /*we load the projection sting in the map and possibly
490     set the axis order*/
491     if( nWFSVersion >= OWS_1_1_0 ) {
492       msLoadProjectionStringEPSG(&(map->projection), pszOutputSRS);
493     } else {
494       msLoadProjectionString(&(map->projection), pszOutputSRS);
495     }
496 
497     nTmp = GetMapserverUnitUsingProj(&(map->projection));
498     if( nTmp != -1 ) {
499       map->units = nTmp;
500     }
501   }
502 
503   msFree(pszOutputSRS);
504   msFree(pszMapSRS);
505   return MS_SUCCESS;
506 }
507 
508 
509 /* msWFSIsLayerSupported()
510 **
511 ** Returns true (1) is this layer meets the requirements to be served as
512 ** a WFS feature type.
513 */
msWFSIsLayerSupported(layerObj * lp)514 int msWFSIsLayerSupported(layerObj *lp)
515 {
516   /* In order to be supported, lp->type must be specified, even for
517   ** layers that are OGR, SDE, SDO, etc connections.
518   */
519   if ((lp->type == MS_LAYER_POINT ||
520        lp->type == MS_LAYER_LINE ||
521        lp->type == MS_LAYER_POLYGON ) &&
522       lp->connectiontype != MS_WMS &&
523       lp->connectiontype != MS_GRATICULE) {
524     return 1; /* true */
525   }
526 
527   return 0; /* false */
528 }
529 
msWFSIsLayerAllowed(layerObj * lp,owsRequestObj * ows_request)530 static int msWFSIsLayerAllowed(layerObj *lp, owsRequestObj *ows_request)
531 {
532     return msWFSIsLayerSupported(lp) &&
533            (msIntegerInArray(lp->index, ows_request->enabled_layers, ows_request->numlayers));
534 }
535 
msWFSGetLayerByName(mapObj * map,owsRequestObj * ows_request,const char * name)536 static layerObj* msWFSGetLayerByName(mapObj* map, owsRequestObj *ows_request, const char* name)
537 {
538     int j;
539     for (j=0; j<map->numlayers; j++) {
540         layerObj *lp;
541 
542         lp = GET_LAYER(map, j);
543 
544         if (msWFSIsLayerAllowed(lp, ows_request) && lp->name && (strcasecmp(lp->name, name) == 0)) {
545             return lp;
546         }
547     }
548     return NULL;
549 }
550 
551 /*
552 ** msWFSDumpLayer()
553 */
msWFSDumpLayer(mapObj * map,layerObj * lp,const char * script_url_encoded)554 int msWFSDumpLayer(mapObj *map, layerObj *lp, const char *script_url_encoded)
555 {
556   rectObj ext;
557   char *pszWfsSrs = NULL;
558   projectionObj poWfs;
559 
560   msIO_printf("    <FeatureType>\n");
561 
562   if (lp->name && strlen(lp->name) > 0 &&
563       (msIsXMLTagValid(lp->name) == MS_FALSE || isdigit(lp->name[0])))
564     msIO_fprintf(stdout, "<!-- WARNING: The layer name '%s' might contain spaces or "
565                  "invalid characters or may start with a number. This could lead to potential problems. -->\n",lp->name);
566 
567   msOWSPrintEncodeParam(stdout, "LAYER.NAME", lp->name, OWS_WARN,
568                         "        <Name>%s</Name>\n", NULL);
569 
570   msOWSPrintEncodeMetadata(stdout, &(lp->metadata), "FO", "title",
571                            OWS_WARN, "        <Title>%s</Title>\n", lp->name);
572 
573   msOWSPrintEncodeMetadata(stdout, &(lp->metadata), "FO", "abstract",
574                            OWS_NOERR, "        <Abstract>%s</Abstract>\n", NULL);
575 
576   msOWSPrintEncodeMetadataList(stdout, &(lp->metadata), "FO",
577                                "keywordlist",
578                                "        <Keywords>\n",
579                                "        </Keywords>\n",
580                                "          %s\n", NULL);
581 
582   /* In WFS, every layer must have exactly one SRS and there is none at */
583   /* the top level contrary to WMS */
584   /*  */
585   /* So here is the way we'll deal with SRS: */
586   /* 1- If a top-level map projection (or wfs_srs metadata) is set then */
587   /* all layers are advertized in the map's projection and they will */
588   /* be reprojected on the fly in the GetFeature request. */
589   /* 2- If there is no top-level map projection (or wfs_srs metadata) then */
590   /* each layer is advertized in its own projection as defined in the */
591   /* layer's projection object or wfs_srs metadata. */
592   /*  */
593 
594   /* if Map has a SRS,  Use it for all layers. */
595   msOWSGetEPSGProj(&(map->projection),&(map->web.metadata),"FO",MS_TRUE, &pszWfsSrs);
596   if(!pszWfsSrs) {
597     /* Map has no SRS.  Use layer SRS or produce a warning. */
598     msOWSGetEPSGProj(&(lp->projection),&(lp->metadata), "FO", MS_TRUE, &pszWfsSrs);
599   }
600 
601   msOWSPrintEncodeParam(stdout, "(at least one of) MAP.PROJECTION, LAYER.PROJECTION or wfs_srs metadata",
602                         pszWfsSrs, OWS_WARN, "        <SRS>%s</SRS>\n", NULL);
603 
604   /* If layer has no proj set then use map->proj for bounding box. */
605   if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS) {
606     msInitProjection(&poWfs);
607     msProjectionInheritContextFrom(&poWfs, &(map->projection));
608     if (pszWfsSrs != NULL)
609       msLoadProjectionString(&(poWfs), pszWfsSrs);
610 
611     if(lp->projection.numargs > 0) {
612       msOWSPrintLatLonBoundingBox(stdout, "        ", &(ext),
613                                   &(lp->projection), &(poWfs), OWS_WFS);
614     } else {
615       msOWSPrintLatLonBoundingBox(stdout, "        ", &(ext),
616                                   &(map->projection), &(poWfs), OWS_WFS);
617     }
618     msFreeProjection(&poWfs);
619   } else {
620     msIO_printf("<!-- WARNING: Optional LatLongBoundingBox could not be established for this layer.  Consider setting the EXTENT in the LAYER object, or wfs_extent metadata. Also check that your data exists in the DATA statement -->\n");
621   }
622 
623   if (! msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_href"))
624     msMetadataSetGetMetadataURL(lp, script_url_encoded);
625 
626   msOWSPrintURLType(stdout, &(lp->metadata), "FO", "metadataurl",
627                     OWS_WARN, NULL, "MetadataURL", " type=\"%s\"",
628                     NULL, NULL, " format=\"%s\"", "%s",
629                     MS_TRUE, MS_FALSE, MS_FALSE, MS_TRUE, MS_TRUE,
630                     NULL, NULL, NULL, NULL, NULL, "        ");
631 
632   if (msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid") == NULL) {
633     msIO_fprintf(stdout, "<!-- WARNING: Required Feature Id attribute (fid) not specified for this feature type. Make sure you set one of wfs_featureid, ows_featureid or gml_featureid metadata. -->\n");
634   }
635 
636   msIO_printf("    </FeatureType>\n");
637 
638   msFree(pszWfsSrs);
639   return MS_SUCCESS;
640 }
641 
642 /*
643 ** msWFSHandleUpdateSequence()
644 */
msWFSHandleUpdateSequence(mapObj * map,wfsParamsObj * params,const char * pszFunction)645 int msWFSHandleUpdateSequence(mapObj *map, wfsParamsObj *params, const char* pszFunction)
646 {
647   /* -------------------------------------------------------------------- */
648   /*      Handle updatesequence                                           */
649   /* -------------------------------------------------------------------- */
650 
651   const char* updatesequence = msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence");
652 
653   if (params->pszUpdateSequence != NULL) {
654     int i = msOWSNegotiateUpdateSequence(params->pszUpdateSequence, updatesequence);
655     if (i == 0) { /* current */
656       msSetError(MS_WFSERR, "UPDATESEQUENCE parameter (%s) is equal to server (%s)", pszFunction, params->pszUpdateSequence, updatesequence);
657       /* FIXME? : according to table 7 of OWS 1.1, we should return a service */
658       /* metadata document with only “version” and “updateSequence” parameters */
659       return msWFSException(map, "updatesequence", "CurrentUpdateSequence", params->pszVersion);
660     }
661     if (i > 0) { /* invalid */
662       msSetError(MS_WFSERR, "UPDATESEQUENCE parameter (%s) is higher than server (%s)", pszFunction, params->pszUpdateSequence, updatesequence);
663       /* locator must be NULL. See Table 25 of OWS 1.1 */
664       return msWFSExceptionNoLocator(map, "updatesequence", MS_OWS_ERROR_INVALID_UPDATE_SEQUENCE, params->pszVersion);
665     }
666   }
667 
668   return MS_SUCCESS;
669 }
670 
671 
672 /*
673 ** msWFSGetCapabilitiesNegotiateVersion()
674 */
msWFSGetCapabilitiesNegotiateVersion(mapObj * map,wfsParamsObj * wfsparams)675 static int msWFSGetCapabilitiesNegotiateVersion(mapObj *map, wfsParamsObj *wfsparams)
676 {
677   int iVersion = -1;
678   char tmpString[OWS_VERSION_MAXLEN];
679 
680   /* acceptversions: do OWS Common style of version negotiation */
681   if (wfsparams->pszAcceptVersions && strlen(wfsparams->pszAcceptVersions) > 0) {
682     char **tokens;
683     int i, j;
684 
685     tokens = msStringSplit(wfsparams->pszAcceptVersions, ',', &j);
686     for (i=0; i<j; i++) {
687       iVersion = msOWSParseVersionString(tokens[i]);
688 
689       if (iVersion < 0) {
690         msSetError(MS_WFSERR, "Invalid version format : %s.", "msWFSGetCapabilities()", tokens[i]);
691         msFreeCharArray(tokens, j);
692         return msWFSException(map, "acceptversions", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,NULL);
693       }
694 
695       /* negotiate version */
696       iVersion = msOWSCommonNegotiateVersion(iVersion, wfsSupportedVersions, wfsNumSupportedVersions);
697       if (iVersion != -1)
698         break;
699     }
700     msFreeCharArray(tokens, j);
701     if(iVersion == -1) {
702       msSetError(MS_WFSERR, "ACCEPTVERSIONS list (%s) does not match supported versions", "msWFSGetCapabilities()", wfsparams->pszAcceptVersions);
703       /* locator must be NULL. See Table 25 of OWS 1.1 */
704       return msWFSExceptionNoLocator(map, "acceptversions", MS_OWS_ERROR_VERSION_NEGOTIATION_FAILED,NULL);
705     }
706   } else {
707     /* negotiate version */
708     int tmpInt;
709     iVersion = msOWSParseVersionString(wfsparams->pszVersion);
710     if( iVersion < 0 )
711     {
712         return msWFSException(map, "version", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, NULL);
713     }
714     tmpInt = msOWSCommonNegotiateVersion(iVersion, wfsSupportedVersions, wfsNumSupportedVersions);
715     /* Old style negociation : paragraph D.11 of OWS 1.1.0 spec */
716     if( tmpInt < 0 )
717     {
718         int i;
719         for( i = 0 ; i < wfsNumSupportedVersions; i++ )
720         {
721             if( iVersion >= wfsSupportedVersions[i] )
722             {
723                 iVersion = wfsSupportedVersions[i];
724                 break;
725             }
726         }
727         if( i == wfsNumSupportedVersions )
728             iVersion = wfsSupportedVersions[wfsNumSupportedVersions - 1];
729     }
730   }
731 
732   /* set result as string and carry on */
733   if (wfsparams->pszVersion)
734     msFree(wfsparams->pszVersion);
735   wfsparams->pszVersion = msStrdup(msOWSGetVersionString(iVersion, tmpString));
736 
737   return MS_SUCCESS;
738 }
739 
740 
741 /*
742 ** msWFSGetCapabilities()
743 */
msWFSGetCapabilities(mapObj * map,wfsParamsObj * wfsparams,cgiRequestObj * req,owsRequestObj * ows_request)744 int msWFSGetCapabilities(mapObj *map, wfsParamsObj *wfsparams, cgiRequestObj *req, owsRequestObj *ows_request)
745 {
746   char *script_url=NULL, *script_url_encoded;
747   const char *updatesequence=NULL;
748   const char *wmtver=NULL;
749   char *formats_list;
750   int ret;
751   int iVersion;
752   int i=0;
753 
754   ret = msWFSGetCapabilitiesNegotiateVersion(map, wfsparams);
755   if( ret != MS_SUCCESS )
756       return ret;
757 
758   iVersion = msOWSParseVersionString(wfsparams->pszVersion);
759   if( iVersion == OWS_2_0_0 )
760     return msWFSGetCapabilities20( map, wfsparams, req, ows_request);
761   if( iVersion == OWS_1_1_0 )
762     return msWFSGetCapabilities11( map, wfsparams, req, ows_request);
763 
764   /* Decide which version we're going to return... only 1.0.0 for now */
765   wmtver = "1.0.0";
766 
767   /* We need this server's onlineresource. */
768   if ((script_url=msOWSGetOnlineResource(map, "FO", "onlineresource", req)) == NULL ||
769       (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) {
770     msSetError(MS_WFSERR, "Server URL not found", "msWFSGetCapabilities()");
771     return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, wmtver);
772   }
773   free(script_url);
774   script_url = NULL;
775 
776   ret = msWFSHandleUpdateSequence(map, wfsparams, "msWFSGetCapabilities()");
777   if( ret != MS_SUCCESS )
778   {
779       free(script_url_encoded);
780       return ret;
781   }
782 
783   msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
784   msIO_sendHeaders();
785 
786   msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
787 
788   updatesequence = msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence");
789   msIO_printf("<WFS_Capabilities \n"
790               "   version=\"%s\" \n"
791               "   updateSequence=\"%s\" \n"
792               "   xmlns=\"http://www.opengis.net/wfs\" \n"
793               "   xmlns:ogc=\"http://www.opengis.net/ogc\" \n"
794               "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
795               "   xsi:schemaLocation=\"http://www.opengis.net/wfs %s/wfs/%s/WFS-capabilities.xsd\">\n",
796               wmtver, updatesequence ? updatesequence : "0",
797               msOWSGetSchemasLocation(map), wmtver);
798 
799   /* Report MapServer Version Information */
800   msIO_printf("\n<!-- %s -->\n\n", msGetVersion());
801 
802   /*
803   ** SERVICE definition
804   */
805   msIO_printf("<Service>\n");
806   msIO_printf("  <Name>MapServer WFS</Name>\n");
807 
808   /* the majority of this section is dependent on appropriately named metadata in the WEB object */
809   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "title",
810                            OWS_WARN, "  <Title>%s</Title>\n", map->name);
811   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "abstract",
812                            OWS_NOERR, "  <Abstract>%s</Abstract>\n", NULL);
813 
814   msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "FO",
815                                "keywordlist",
816                                "  <Keywords>\n", "  </Keywords>\n",
817                                "    %s\n", NULL);
818 
819   /* Service/onlineresource */
820   /* Defaults to same as request onlineresource if wfs_service_onlineresource  */
821   /* is not set. */
822   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata),
823                            "FO", "service_onlineresource", OWS_NOERR,
824                            "  <OnlineResource>%s</OnlineResource>\n",
825                            script_url_encoded);
826 
827   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "fees",
828                            OWS_NOERR, "  <Fees>%s</Fees>\n", NULL);
829 
830   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO",
831                            "accessconstraints", OWS_NOERR,
832                            "  <AccessConstraints>%s</AccessConstraints>\n",
833                            NULL);
834 
835   msIO_printf("</Service>\n\n");
836 
837   /*
838   ** CAPABILITY definitions: list of supported requests
839   */
840 
841   msIO_printf("<Capability>\n");
842 
843   msIO_printf("  <Request>\n");
844   msWFSPrintRequestCap(wmtver, "GetCapabilities", script_url_encoded,
845                        NULL, NULL);
846   /* msWFSPrintRequestCap(wmtver, "DescribeFeatureType", script_url_encoded, "SchemaDescriptionLanguage", "XMLSCHEMA", "SFE_XMLSCHEMA", NULL); */
847   /* msWFSPrintRequestCap(wmtver, "GetFeature", script_url_encoded, "ResultFormat", "GML2", "GML3", NULL); */
848 
849   /* don't advertise the GML3 or GML for SFE support */
850   if (msOWSRequestIsEnabled(map, NULL, "F", "DescribeFeatureType", MS_TRUE))
851     msWFSPrintRequestCap(wmtver, "DescribeFeatureType", script_url_encoded, "SchemaDescriptionLanguage", "XMLSCHEMA" );
852 
853   if (msOWSRequestIsEnabled(map, NULL, "F", "GetFeature", MS_TRUE)) {
854     formats_list = msWFSGetOutputFormatList( map, NULL, OWS_1_0_0 );
855     msWFSPrintRequestCap(wmtver, "GetFeature", script_url_encoded, "ResultFormat", formats_list );
856     msFree( formats_list );
857   }
858 
859   msIO_printf("  </Request>\n");
860   msIO_printf("</Capability>\n\n");
861 
862   /*
863   ** FeatureTypeList: layers
864   */
865 
866   msIO_printf("<FeatureTypeList>\n");
867 
868   /* Operations supported... set default at top-level, and more operations */
869   /* can be added inside each layer... for MapServer only query is supported */
870   msIO_printf("  <Operations>\n");
871   msIO_printf("    <Query/>\n");
872   msIO_printf("  </Operations>\n");
873 
874   for(i=0; i<map->numlayers; i++) {
875     layerObj *lp;
876     lp = GET_LAYER(map, i);
877 
878     if (lp->status == MS_DELETE)
879       continue;
880 
881     if (msWFSIsLayerAllowed(lp, ows_request)) {
882       msWFSDumpLayer(map, lp, script_url_encoded);
883     }
884   }
885 
886   msIO_printf("</FeatureTypeList>\n\n");
887 
888 
889   /*
890   ** OGC Filter Capabilities ... for now we support only BBOX
891   */
892 
893   msIO_printf("<ogc:Filter_Capabilities>\n");
894   msIO_printf("  <ogc:Spatial_Capabilities>\n");
895   msIO_printf("    <ogc:Spatial_Operators>\n");
896 #ifdef USE_GEOS
897   msIO_printf("      <ogc:Equals/>\n");
898   msIO_printf("      <ogc:Disjoint/>\n");
899   msIO_printf("      <ogc:Touches/>\n");
900   msIO_printf("      <ogc:Within/>\n");
901   msIO_printf("      <ogc:Overlaps/>\n");
902   msIO_printf("      <ogc:Crosses/>\n");
903   msIO_printf("      <ogc:Intersect/>\n");
904   msIO_printf("      <ogc:Contains/>\n");
905   msIO_printf("      <ogc:DWithin/>\n");
906 #endif
907   msIO_printf("      <ogc:BBOX/>\n");
908   msIO_printf("    </ogc:Spatial_Operators>\n");
909   msIO_printf("  </ogc:Spatial_Capabilities>\n");
910 
911   msIO_printf("  <ogc:Scalar_Capabilities>\n");
912   msIO_printf("    <ogc:Logical_Operators />\n");
913   msIO_printf("    <ogc:Comparison_Operators>\n");
914   msIO_printf("      <ogc:Simple_Comparisons />\n");
915   msIO_printf("      <ogc:Like />\n");
916   msIO_printf("      <ogc:Between />\n");
917   msIO_printf("    </ogc:Comparison_Operators>\n");
918   msIO_printf("  </ogc:Scalar_Capabilities>\n");
919 
920   msIO_printf("</ogc:Filter_Capabilities>\n\n");
921 
922   /*
923   ** Done!
924   */
925   msIO_printf("</WFS_Capabilities>\n");
926 
927   free(script_url_encoded);
928 
929   return MS_SUCCESS;
930 }
931 
932 /*
933 ** Helper functions for producing XML schema.
934 */
935 
msWFSGetGeometryType(const char * type,OWSGMLVersion outputformat)936 static const char *msWFSGetGeometryType(const char *type, OWSGMLVersion outputformat)
937 {
938   if(!type) return "GeometryPropertyType";
939 
940   if(strcasecmp(type, "point") == 0) {
941     switch(outputformat) {
942       case OWS_GML2:
943       case OWS_GML3:
944       case OWS_GML32:
945         return "PointPropertyType";
946     }
947   } else if(strcasecmp(type, "multipoint") == 0) {
948     switch(outputformat) {
949       case OWS_GML2:
950       case OWS_GML3:
951       case OWS_GML32:
952         return "MultiPointPropertyType";
953     }
954   } else if(strcasecmp(type, "line") == 0) {
955     switch(outputformat) {
956       case OWS_GML2:
957         return "LineStringPropertyType";
958       case OWS_GML3:
959       case OWS_GML32:
960         return "CurvePropertyType";
961     }
962   } else if(strcasecmp(type, "multiline") == 0) {
963     switch(outputformat) {
964       case OWS_GML2:
965         return "MultiLineStringPropertyType";
966       case OWS_GML3:
967       case OWS_GML32:
968         return "MultiCurvePropertyType";
969     }
970   } else if(strcasecmp(type, "polygon") == 0) {
971     switch(outputformat) {
972       case OWS_GML2:
973         return "PolygonPropertyType";
974       case OWS_GML3:
975       case OWS_GML32:
976         return "SurfacePropertyType";
977     }
978   } else if(strcasecmp(type, "multipolygon") == 0) {
979     switch(outputformat) {
980       case OWS_GML2:
981         return "MultiPolygonPropertyType";
982       case OWS_GML3:
983       case OWS_GML32:
984         return "MultiSurfacePropertyType";
985     }
986   }
987 
988   return "???unknown???";
989 }
990 
msWFSWriteGeometryElement(FILE * stream,gmlGeometryListObj * geometryList,OWSGMLVersion outputformat,const char * tab)991 static void msWFSWriteGeometryElement(FILE *stream, gmlGeometryListObj *geometryList, OWSGMLVersion outputformat, const char *tab)
992 {
993   int i;
994   gmlGeometryObj *geometry=NULL;
995 
996   if(!stream || !tab) return;
997   if(geometryList && geometryList->numgeometries == 1 && strcasecmp(geometryList->geometries[0].name, "none") == 0) return;
998 
999   if(geometryList->numgeometries == 1) {
1000     geometry = &(geometryList->geometries[0]);
1001     msIO_fprintf(stream, "%s<element name=\"%s\" type=\"gml:%s\" minOccurs=\"%d\"", tab, geometry->name, msWFSGetGeometryType(geometry->type, outputformat), geometry->occurmin);
1002     if(geometry->occurmax == OWS_GML_OCCUR_UNBOUNDED)
1003       msIO_fprintf(stream, " maxOccurs=\"unbounded\"/>\n");
1004     else
1005       msIO_fprintf(stream, " maxOccurs=\"%d\"/>\n", geometry->occurmax);
1006   } else {
1007     msIO_fprintf(stream, "%s<choice>\n", tab);
1008     for(i=0; i<geometryList->numgeometries; i++) {
1009       geometry = &(geometryList->geometries[i]);
1010 
1011       msIO_fprintf(stream, "  %s<element name=\"%s\" type=\"gml:%s\" minOccurs=\"%d\"", tab, geometry->name, msWFSGetGeometryType(geometry->type, outputformat), geometry->occurmin);
1012       if(geometry->occurmax == OWS_GML_OCCUR_UNBOUNDED)
1013         msIO_fprintf(stream, " maxOccurs=\"unbounded\"/>\n");
1014       else
1015         msIO_fprintf(stream, " maxOccurs=\"%d\"/>\n", geometry->occurmax);
1016     }
1017     msIO_fprintf(stream, "%s</choice>\n", tab);
1018   }
1019 
1020   return;
1021 }
1022 
msWFSGetGMLVersionFromSchemaVersion(WFSSchemaVersion outputformat)1023 static OWSGMLVersion msWFSGetGMLVersionFromSchemaVersion(WFSSchemaVersion outputformat)
1024 {
1025     switch( outputformat )
1026     {
1027         case OWS_DEFAULT_SCHEMA:
1028             return OWS_GML2;
1029         case OWS_SFE_SCHEMA:
1030             return OWS_GML3;
1031         case OWS_GML32_SFE_SCHEMA:
1032             return OWS_GML32;
1033     }
1034     return OWS_GML2;
1035 }
1036 
msWFSSchemaWriteGeometryElement(FILE * stream,gmlGeometryListObj * geometryList,WFSSchemaVersion outputformat,const char * tab)1037 static void msWFSSchemaWriteGeometryElement(FILE *stream, gmlGeometryListObj *geometryList, WFSSchemaVersion outputformat, const char *tab)
1038 {
1039   OWSGMLVersion gmlversion = msWFSGetGMLVersionFromSchemaVersion(outputformat);
1040   msWFSWriteGeometryElement(stream,geometryList,gmlversion,tab);
1041 }
1042 
msWFSMapServTypeToXMLType(const char * type)1043 static const char* msWFSMapServTypeToXMLType(const char* type)
1044 {
1045     const char *element_type = "string";
1046     /* Map from MapServer types to XSD types */
1047     if( strcasecmp(type,"Integer") == 0 )
1048       element_type = "integer";
1049     /* Note : xs:int and xs:integer differ */
1050     else if ( EQUAL(type,"int") )
1051       element_type = "int";
1052     if( strcasecmp(type,"Long") == 0 ) /* 64bit integer */
1053       element_type = "long";
1054     else if( EQUAL(type,"Real") ||
1055              EQUAL(type,"double") /* just in case someone provided the xsd type directly */ )
1056       element_type = "double";
1057     else if( EQUAL(type,"Character") )
1058       element_type = "string";
1059     else if( EQUAL(type,"Date") )
1060       element_type = "date";
1061     else if( EQUAL(type,"Time") )
1062       element_type = "time";
1063     else if( EQUAL(type,"DateTime") )
1064       element_type = "dateTime";
1065     else if( EQUAL(type,"Boolean") )
1066       element_type = "boolean";
1067     return element_type;
1068 }
1069 
msWFSWriteItemElement(FILE * stream,gmlItemObj * item,const char * tab,WFSSchemaVersion outputformat,int is_nillable)1070 static void msWFSWriteItemElement(FILE *stream, gmlItemObj *item, const char *tab,
1071                                   WFSSchemaVersion outputformat, int is_nillable)
1072 {
1073   const char *element_name;
1074   const char *element_type = "string";
1075   const char *pszMinOccurs = "";
1076   const char* pszNillable = "";
1077 
1078   if(!stream || !item || !tab) return;
1079   if(!item->visible) return; /* not exposing this attribute */
1080   if(item->template) return; /* can't adequately deal with templated items yet */
1081 
1082   if(item->alias) /* TODO: what about name spaces embedded in the alias? */
1083     element_name = item->alias;
1084   else
1085     element_name = item->name;
1086 
1087   if(item->type)
1088   {
1089     if( outputformat == OWS_GML32_SFE_SCHEMA &&
1090         (EQUAL(item->type,"Date") || EQUAL(item->type,"Time") || EQUAL(item->type,"DateTime")) )
1091       element_type = "gml:TimeInstantType";
1092     else
1093       element_type = msWFSMapServTypeToXMLType(item->type);
1094   }
1095 
1096   if( item->minOccurs == 0 )
1097       pszMinOccurs = " minOccurs=\"0\"";
1098 
1099   if( is_nillable )
1100       pszNillable = " nillable=\"true\"";
1101 
1102   msIO_fprintf(stream, "%s<element name=\"%s\"%s%s type=\"%s\"/>\n", tab, element_name, pszMinOccurs, pszNillable, element_type);
1103 
1104   return;
1105 }
1106 
msWFSWriteConstantElement(FILE * stream,gmlConstantObj * constant,const char * tab)1107 static void msWFSWriteConstantElement(FILE *stream, gmlConstantObj *constant, const char *tab)
1108 {
1109   const char *element_type = "string";
1110 
1111   if(!stream || !constant || !tab) return;
1112 
1113   if(constant->type)
1114     element_type = msWFSMapServTypeToXMLType(constant->type);
1115 
1116   msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s\"/>\n", tab, constant->name, element_type);
1117 
1118   return;
1119 }
1120 
msWFSWriteGroupElement(FILE * stream,gmlGroupObj * group,const char * tab,const char * namespace)1121 static void msWFSWriteGroupElement(FILE *stream, gmlGroupObj *group, const char *tab, const char *namespace)
1122 {
1123   if(group->type)
1124     msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s:%s\"/>\n", tab, group->name, namespace, group->type);
1125   else
1126     msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s:%sType\"/>\n", tab, group->name, namespace, group->name);
1127 
1128   return;
1129 }
1130 
msWFSWriteGroupElementType(FILE * stream,gmlGroupObj * group,gmlItemListObj * itemList,gmlConstantListObj * constantList,const char * tab,WFSSchemaVersion outputformat)1131 static void msWFSWriteGroupElementType(FILE *stream, gmlGroupObj *group,
1132                                        gmlItemListObj *itemList,
1133                                        gmlConstantListObj *constantList,
1134                                        const char *tab,
1135                                        WFSSchemaVersion outputformat)
1136 {
1137   int i, j;
1138   char *element_tab;
1139 
1140   gmlItemObj *item=NULL;
1141   gmlConstantObj *constant=NULL;
1142 
1143   /* setup the element tab */
1144   element_tab = (char *) msSmallMalloc(sizeof(char)*strlen(tab)+5);
1145   sprintf(element_tab, "%s    ", tab);
1146 
1147   if(group->type)
1148     msIO_fprintf(stream, "%s<complexType name=\"%s\">\n", tab, group->type);
1149   else
1150     msIO_fprintf(stream, "%s<complexType name=\"%sType\">\n", tab, group->name);
1151 
1152   msIO_fprintf(stream, "%s  <sequence>\n", tab);
1153 
1154   /* now the items/constants (e.g. elements) in the group */
1155   for(i=0; i<group->numitems; i++) {
1156     for(j=0; j<constantList->numconstants; j++) { /* find the right gmlConstantObj */
1157       constant = &(constantList->constants[j]);
1158       if(strcasecmp(constant->name, group->items[i]) == 0) {
1159         msWFSWriteConstantElement(stream, constant, element_tab);
1160         break;
1161       }
1162     }
1163     if(j != constantList->numconstants) continue; /* found this item */
1164     for(j=0; j<itemList->numitems; j++) { /* find the right gmlItemObj */
1165       item = &(itemList->items[j]);
1166       if(strcasecmp(item->name, group->items[i]) == 0) {
1167         msWFSWriteItemElement(stream, item, element_tab, outputformat, MS_FALSE);
1168         break;
1169       }
1170     }
1171   }
1172 
1173   msIO_fprintf(stream, "%s  </sequence>\n", tab);
1174   msIO_fprintf(stream, "%s</complexType>\n", tab);
1175   free(element_tab);
1176 
1177   return;
1178 }
1179 
msWFSGetGMLSchemaLocation(OWSGMLVersion outputformat)1180 static const char* msWFSGetGMLSchemaLocation(OWSGMLVersion outputformat)
1181 {
1182     switch( outputformat )
1183     {
1184         case OWS_GML2:
1185             return MS_OWSCOMMON_GML_212_SCHEMA_LOCATION;
1186         case OWS_GML3:
1187             return MS_OWSCOMMON_GML_311_SCHEMA_LOCATION;
1188         case OWS_GML32:
1189             return MS_OWSCOMMON_GML_321_SCHEMA_LOCATION;
1190     }
1191     return "/unknown.xsd";
1192 }
1193 
msWFSGetGMLNamespaceURI(WFSSchemaVersion outputformat)1194 static const char* msWFSGetGMLNamespaceURI(WFSSchemaVersion outputformat)
1195 {
1196     switch( outputformat )
1197     {
1198         case OWS_DEFAULT_SCHEMA:
1199             return MS_OWSCOMMON_GML_NAMESPACE_URI;
1200         case OWS_SFE_SCHEMA:
1201             return MS_OWSCOMMON_GML_NAMESPACE_URI;
1202         case OWS_GML32_SFE_SCHEMA:
1203             return MS_OWSCOMMON_GML_32_NAMESPACE_URI;
1204     }
1205     return "http://unknown";
1206 }
1207 
msWFSGetGMLNamespaceURIFromGMLVersion(OWSGMLVersion outputformat)1208 static const char* msWFSGetGMLNamespaceURIFromGMLVersion(OWSGMLVersion outputformat)
1209 {
1210     switch( outputformat )
1211     {
1212         case OWS_GML2:
1213             return MS_OWSCOMMON_GML_NAMESPACE_URI;
1214         case OWS_GML3:
1215             return MS_OWSCOMMON_GML_NAMESPACE_URI;
1216         case OWS_GML32:
1217             return MS_OWSCOMMON_GML_32_NAMESPACE_URI;
1218     }
1219     return "http://unknown";
1220 }
1221 
msWFS_NS_printf(const char * prefix,const char * uri)1222 static void msWFS_NS_printf(const char* prefix, const char* uri)
1223 {
1224     if( prefix == NULL )
1225         msIO_printf("   xmlns=\"%s\"\n", uri);
1226     else
1227         msIO_printf("   xmlns:%s=\"%s\"\n", prefix, uri);
1228 }
1229 
msWFSPrintAdditionalNamespaces(const gmlNamespaceListObj * namespaceList)1230 static void msWFSPrintAdditionalNamespaces(const gmlNamespaceListObj *namespaceList)
1231 {
1232     int i;
1233     /* any additional namespaces */
1234     for(i=0; i<namespaceList->numnamespaces; i++) {
1235       if(namespaceList->namespaces[i].uri) {
1236         char *uri_encoded=NULL;
1237 
1238         uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
1239         msWFS_NS_printf(namespaceList->namespaces[i].prefix, uri_encoded);
1240         msFree(uri_encoded);
1241       }
1242     }
1243 }
1244 
msWFSStripNS(const char * name)1245 static const char* msWFSStripNS(const char* name)
1246 {
1247     const char* pszColon = strchr(name, ':');
1248     const char* pszSlash = strchr(name, '/');
1249     if( pszColon && (pszSlash == NULL || pszColon < pszSlash) )
1250       return pszColon + 1;
1251     return name;
1252 }
1253 
1254 /*
1255 ** msWFSDescribeFeatureType()
1256 */
1257 static
msWFSDescribeFeatureType(mapObj * map,wfsParamsObj * paramsObj,owsRequestObj * ows_request,int nWFSVersion)1258 int msWFSDescribeFeatureType(mapObj *map, wfsParamsObj *paramsObj, owsRequestObj *ows_request,
1259                              int nWFSVersion)
1260 {
1261   int i, numlayers=0;
1262   char **layers = NULL;
1263 
1264   const char *value;
1265   const char *user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
1266   const char *user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
1267   char *user_namespace_uri_encoded = NULL;
1268   const char *collection_name = OWS_WFS_FEATURE_COLLECTION_NAME;
1269   char *encoded;
1270 
1271   WFSSchemaVersion outputformat = OWS_DEFAULT_SCHEMA; /* default output is GML 2.1 compliant schema*/
1272 
1273   gmlNamespaceListObj *namespaceList=NULL; /* for external application schema support */
1274   char *mimetype = NULL;
1275 
1276   if(paramsObj->pszTypeName && numlayers == 0) {
1277     /* Parse comma-delimited list of type names (layers) */
1278     layers = msStringSplit(paramsObj->pszTypeName, ',', &numlayers);
1279     if (numlayers > 0) {
1280       /* strip namespace if there is one :ex TYPENAME=cdf:Other */
1281       for (i=0; i<numlayers; i++) {
1282         char* pszTmp = msStrdup(msWFSStripNS(layers[i]));
1283         free(layers[i]);
1284         layers[i] = pszTmp;
1285       }
1286     }
1287   }
1288 
1289 
1290   if (paramsObj->pszOutputFormat) {
1291     if(strcasecmp(paramsObj->pszOutputFormat, "XMLSCHEMA") == 0 ||
1292         strstr(paramsObj->pszOutputFormat, "gml/2")!= NULL) {
1293       mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/2.1.2");
1294       outputformat = OWS_DEFAULT_SCHEMA;
1295     } else if(strcasecmp(paramsObj->pszOutputFormat, "SFE_XMLSCHEMA") == 0 ||
1296               strstr(paramsObj->pszOutputFormat, "gml/3.1")!= NULL) {
1297       mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/3.1.1");
1298       outputformat = OWS_SFE_SCHEMA;
1299     } else if(strstr(paramsObj->pszOutputFormat, "gml/3.2")!= NULL ||
1300               strstr(paramsObj->pszOutputFormat, "application/gml+xml; version=3.2")!= NULL) {
1301       mimetype = msEncodeHTMLEntities("application/gml+xml; version=3.2");
1302       outputformat = OWS_GML32_SFE_SCHEMA;
1303     } else {
1304       msSetError(MS_WFSERR, "Unsupported DescribeFeatureType outputFormat (%s).", "msWFSDescribeFeatureType()", paramsObj->pszOutputFormat);
1305       if( layers )
1306         msFreeCharArray(layers, numlayers);
1307       msFree(mimetype);
1308       return msWFSException(map, "outputformat", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
1309     }
1310   }
1311   /* If no outputFormat explicitely asked, use a sensible default for the WFS version */
1312   else {
1313     switch( nWFSVersion )
1314     {
1315         case OWS_1_0_0:
1316         default:
1317             mimetype = msEncodeHTMLEntities("text/xml"); /* ERO: why not "text/xml; subtype=gml/2.1.2" ? */
1318             break;
1319 
1320         case OWS_1_1_0:
1321             mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/3.1.1");
1322             outputformat = OWS_SFE_SCHEMA;
1323             break;
1324 
1325         case OWS_2_0_0:
1326             mimetype = msEncodeHTMLEntities("application/gml+xml; version=3.2");
1327             outputformat = OWS_GML32_SFE_SCHEMA;
1328             break;
1329      }
1330   }
1331 
1332   /* Validate layers */
1333   if (numlayers > 0) {
1334     for (i=0; i<numlayers; i++) {
1335       layerObj* lp = msWFSGetLayerByName(map, ows_request, layers[i]);
1336       if ( lp == NULL ) {
1337         msSetError(MS_WFSERR, "Invalid typename (%s). A layer might be disabled for \
1338 this request. Check wfs/ows_enable_request settings.", "msWFSDescribeFeatureType()", layers[i]);/* paramsObj->pszTypeName); */
1339         if( layers )
1340             msFreeCharArray(layers, numlayers);
1341         msFree(mimetype);
1342         return msWFSException(map, "typename", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
1343       }
1344     }
1345   }
1346 
1347   /*
1348   ** retrieve any necessary external namespace/schema configuration information
1349   */
1350   namespaceList = msGMLGetNamespaces(&(map->web), "G");
1351   if (namespaceList == NULL) {
1352     msSetError(MS_MISCERR, "Unable to populate namespace list", "msWFSDescribeFeatureType()");
1353     return MS_FAILURE;
1354   }
1355 
1356   /*
1357   ** DescribeFeatureType response
1358   */
1359 
1360   msIO_setHeader("Content-Type","%s; charset=UTF-8",mimetype);
1361   msIO_sendHeaders();
1362 
1363   if (mimetype)
1364     msFree(mimetype);
1365 
1366   msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
1367 
1368   value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
1369   if(value) user_namespace_uri = value;
1370   user_namespace_uri_encoded = msEncodeHTMLEntities(user_namespace_uri);
1371 
1372   value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
1373   if(value) user_namespace_prefix = value;
1374   if(user_namespace_prefix != NULL && msIsXMLTagValid(user_namespace_prefix) == MS_FALSE)
1375     msIO_printf("<!-- WARNING: The value '%s' is not valid XML namespace. -->\n", user_namespace_prefix);
1376 
1377   msIO_printf("<schema\n"
1378               "   targetNamespace=\"%s\" \n"
1379               "   xmlns:%s=\"%s\" \n",
1380               user_namespace_uri_encoded, user_namespace_prefix,  user_namespace_uri_encoded);
1381   if( nWFSVersion < OWS_2_0_0 )
1382     msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX, MS_OWSCOMMON_OGC_NAMESPACE_URI);
1383   msWFS_NS_printf("xsd", MS_OWSCOMMON_W3C_XS_NAMESPACE_URI);
1384   msWFS_NS_printf(NULL, MS_OWSCOMMON_W3C_XS_NAMESPACE_URI);
1385   msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1386                   msWFSGetGMLNamespaceURI(outputformat) );
1387 
1388   msWFSPrintAdditionalNamespaces(namespaceList);
1389 
1390   msIO_printf("   elementFormDefault=\"qualified\" version=\"0.1\" >\n");
1391 
1392   encoded = msEncodeHTMLEntities( msOWSGetSchemasLocation(map) );
1393 
1394   msIO_printf("\n  <import namespace=\"%s\"\n"
1395               "          schemaLocation=\"%s%s\" />\n",
1396               msWFSGetGMLNamespaceURI(outputformat),
1397               encoded,
1398               msWFSGetGMLSchemaLocation(msWFSGetGMLVersionFromSchemaVersion(outputformat)));
1399 
1400   msFree(encoded);
1401 
1402   /* any additional namespace includes */
1403   for(i=0; i<namespaceList->numnamespaces; i++) {
1404     if(namespaceList->namespaces[i].uri && namespaceList->namespaces[i].schemalocation) {
1405       char *schema_location_encoded=NULL, *uri_encoded=NULL;
1406 
1407       uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
1408       schema_location_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].schemalocation);
1409       msIO_printf("\n  <import namespace=\"%s\"\n schemaLocation=\"%s\" />\n", uri_encoded, schema_location_encoded);
1410       msFree(uri_encoded);
1411       msFree(schema_location_encoded);
1412     }
1413   }
1414 
1415   /* output definition for the default feature container, can't use wfs:FeatureCollection with GML3:
1416      kept here so that the behaviour with wfs1.0 and gml3 output is preserved.
1417      We can use the wfs:FeatureCollection for wfs1.1*/
1418   if(outputformat == OWS_SFE_SCHEMA && nWFSVersion == OWS_1_0_0 ) {
1419     value = msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection");
1420     if(value) collection_name = value;
1421 
1422     msIO_printf("  <element name=\"%s\" type=\"%s:%sType\" substitutionGroup=\"gml:_FeatureCollection\"/>\n", collection_name, user_namespace_prefix, collection_name);
1423     msIO_printf("  <complexType name=\"%sType\">\n", collection_name);
1424     msIO_printf("    <complexContent>\n");
1425     msIO_printf("      <extension base=\"gml:AbstractFeatureCollectionType\">\n");
1426     msIO_printf("        <attribute name=\"version\" type=\"string\" use=\"required\" fixed=\"1.0.0\"/>\n");
1427     msIO_printf("      </extension>\n");
1428     msIO_printf("    </complexContent>\n");
1429     msIO_printf("  </complexType>\n");
1430   }
1431 
1432   /*
1433   ** loop through layers
1434   */
1435   for(i=0; i<map->numlayers; i++)  {
1436     layerObj *lp;
1437     int j, bFound = 0;
1438 
1439     lp = GET_LAYER(map, i);
1440 
1441     for (j=0; j<numlayers && !bFound; j++) {
1442       if ( lp->name && strcasecmp(lp->name, layers[j]) == 0)
1443         bFound = 1;
1444     }
1445 
1446     if ((numlayers == 0 || bFound) && msWFSIsLayerAllowed(lp, ows_request) ) {
1447 
1448       /*
1449       ** OK, describe this layer IF you can open it and retrieve items
1450       */
1451       if (msLayerOpen(lp) == MS_SUCCESS) {
1452         if (msLayerGetItems(lp) == MS_SUCCESS) {
1453           int k;
1454           gmlGroupListObj *groupList=NULL;
1455           gmlItemListObj *itemList=NULL;
1456           gmlConstantListObj *constantList=NULL;
1457           gmlGeometryListObj *geometryList=NULL;
1458           gmlItemObj *item=NULL;
1459           gmlConstantObj *constant=NULL;
1460           char *encoded_name = NULL;
1461 
1462           const char *layer_namespace_prefix;
1463           const char *substitution_group;
1464           char *encoded_type=NULL;
1465 
1466           itemList = msGMLGetItems(lp, "G"); /* GML-related metadata */
1467           constantList = msGMLGetConstants(lp, "G");
1468           groupList = msGMLGetGroups(lp, "G");
1469           geometryList = msGMLGetGeometries(lp, "GFO", MS_TRUE);
1470           if (itemList == NULL || constantList == NULL || groupList == NULL || geometryList == NULL) {
1471             msSetError(MS_MISCERR, "Unable to populate item and group metadata structures", "msWFSDescribeFeatureType()");
1472             return MS_FAILURE;
1473           }
1474 
1475           value = msOWSLookupMetadata(&(lp->metadata), "OFG", "namespace_prefix");
1476           if(value)
1477             layer_namespace_prefix = value;
1478           else
1479             layer_namespace_prefix = user_namespace_prefix;
1480 
1481           /* value = msOWSLookupMetadata(&(lp->metadata), "OFG", "layername"); */
1482           encoded_name = msEncodeHTMLEntities( lp->name );
1483           value = msOWSLookupMetadata(&(lp->metadata), "OFG", "layer_type");
1484           if(value) {
1485             encoded_type = msEncodeHTMLEntities(value);
1486           } else {
1487             size_t sz = strlen(encoded_name) + strlen("Type") + 1;
1488             encoded_type = (char*) msSmallMalloc(sz);
1489             strlcpy(encoded_type, encoded_name, sz);
1490             strlcat(encoded_type, "Type", sz);
1491           }
1492 
1493           switch(outputformat)
1494           {
1495             case OWS_DEFAULT_SCHEMA: /* default GML 2.1.x schema */
1496             case OWS_SFE_SCHEMA: /* reference GML 3.1.1 schema */
1497             default:
1498                 substitution_group = "gml:_Feature";
1499                 break;
1500 
1501             case OWS_GML32_SFE_SCHEMA:  /* reference GML 3.2.1 schema */
1502                 substitution_group = "gml:AbstractFeature";
1503                 break;
1504           }
1505 
1506           msIO_printf("\n"
1507                       "  <element name=\"%s\" \n"
1508                       "           type=\"%s:%s\" \n"
1509                       "           substitutionGroup=\"%s\" />\n\n",
1510                       encoded_name, layer_namespace_prefix, encoded_type,
1511                       substitution_group);
1512           msFree(encoded_type);
1513 
1514           if(strcmp(layer_namespace_prefix, user_namespace_prefix) != 0) {
1515             msFree(encoded_name);
1516             msGMLFreeItems(itemList);
1517             msGMLFreeConstants(constantList);
1518             msGMLFreeGroups(groupList);
1519             msGMLFreeGeometries(geometryList);
1520             continue; /* the rest is defined in an external schema */
1521           }
1522 
1523           msIO_printf("  <complexType name=\"%sType\">\n", encoded_name);
1524           msIO_printf("    <complexContent>\n");
1525           msIO_printf("      <extension base=\"gml:AbstractFeatureType\">\n");
1526           msIO_printf("        <sequence>\n");
1527 
1528           /* write the geometry schema element(s) */
1529           msWFSSchemaWriteGeometryElement(stdout, geometryList, outputformat, "          ");
1530 
1531           /* write the constant-based schema elements */
1532           for(k=0; k<constantList->numconstants; k++) {
1533             constant = &(constantList->constants[k]);
1534             if(msItemInGroups(constant->name, groupList) == MS_FALSE)
1535               msWFSWriteConstantElement(stdout, constant, "          ");
1536           }
1537 
1538           /* write the item-based schema elements */
1539           for(k=0; k<itemList->numitems; k++) {
1540             item = &(itemList->items[k]);
1541             if(msItemInGroups(item->name, groupList) == MS_FALSE) {
1542               int nillable = MS_FALSE;
1543               char mdname[256];
1544               const char* pszNillable;
1545               snprintf(mdname, sizeof(mdname), "%s_nillable", item->name);
1546               pszNillable = msOWSLookupMetadata(&(lp->metadata), "G", mdname);
1547               if( pszNillable && strcasecmp(pszNillable, "true") == 0 )
1548                   nillable = MS_TRUE;
1549               msWFSWriteItemElement(stdout, item, "          ", outputformat, nillable);
1550             }
1551           }
1552 
1553           for(k=0; k<groupList->numgroups; k++)
1554             msWFSWriteGroupElement(stdout, &(groupList->groups[k]), "          ", user_namespace_prefix);
1555 
1556           msIO_printf("        </sequence>\n");
1557           msIO_printf("      </extension>\n");
1558           msIO_printf("    </complexContent>\n");
1559           msIO_printf("  </complexType>\n");
1560 
1561           /* any group types */
1562           for(k=0; k<groupList->numgroups; k++)
1563             msWFSWriteGroupElementType(stdout, &(groupList->groups[k]), itemList, constantList, "  ", outputformat);
1564 
1565           msGMLFreeItems(itemList);
1566           msGMLFreeConstants(constantList);
1567           msGMLFreeGroups(groupList);
1568           msGMLFreeGeometries(geometryList);
1569 
1570           msFree(encoded_name);
1571         }
1572 
1573         msLayerClose(lp);
1574       } else {
1575         msIO_printf("\n\n<!-- ERROR: Failed opening layer %s -->\n\n", lp->name);
1576       }
1577     }
1578   }
1579 
1580   /*
1581   ** Done!
1582   */
1583   msIO_printf("\n</schema>\n");
1584 
1585   msFree(user_namespace_uri_encoded);
1586 
1587   if(layers)
1588     msFreeCharArray(layers, numlayers);
1589 
1590   msGMLFreeNamespaces(namespaceList);
1591 
1592   return MS_SUCCESS;
1593 }
1594 
1595 /*
1596 ** msWFSGetFeature_GMLPreamble()
1597 **
1598 ** Generate the GML preamble up to the first feature for the builtin
1599 ** WFS GML support.
1600 */
1601 
1602 typedef struct {
1603   const char *user_namespace_prefix;
1604   const char *user_namespace_uri;
1605   char       *user_namespace_uri_encoded;
1606   const char *collection_name;
1607   const char *typename;
1608   char       *script_url, *script_url_encoded;
1609   const char *output_mime_type;
1610   const char *output_schema_format;
1611 } WFSGMLInfo;
1612 
1613 
msWFSPrintURLAndXMLEncoded(const char * str)1614 static void msWFSPrintURLAndXMLEncoded(const char* str)
1615 {
1616     char* url_encoded = msEncodeUrl(str);
1617     char* xml_encoded = msEncodeHTMLEntities(url_encoded);
1618     msIO_printf("%s", xml_encoded);
1619     msFree(xml_encoded);
1620     msFree(url_encoded);
1621 }
1622 
msWFSGetFeature_PrintBasePrevNextURI(cgiRequestObj * req,WFSGMLInfo * gmlinfo,wfsParamsObj * paramsObj,const char * encoded_version,const char * encoded_typename,const char * encoded_mime_type)1623 static void msWFSGetFeature_PrintBasePrevNextURI(cgiRequestObj *req,
1624                                                  WFSGMLInfo *gmlinfo,
1625                                                  wfsParamsObj *paramsObj,
1626                                                  const char* encoded_version,
1627                                                  const char* encoded_typename,
1628                                                  const char* encoded_mime_type)
1629 {
1630     int i;
1631     int bFirstArg = MS_TRUE;
1632     msIO_printf("%s", gmlinfo->script_url_encoded);
1633 
1634     if( req->postrequest != NULL )
1635     {
1636         msIO_printf("SERVICE=WFS&amp;VERSION=");
1637         msIO_printf("%s", encoded_version);
1638         msIO_printf("&amp;REQUEST=");
1639         msIO_printf("%s", paramsObj->pszRequest);
1640         msIO_printf("&amp;TYPENAMES=");
1641         msIO_printf("%s", encoded_typename);
1642         msIO_printf("&amp;OUTPUTFORMAT=");
1643         msIO_printf("%s", encoded_mime_type);
1644         if( paramsObj->pszBbox != NULL )
1645         {
1646             msIO_printf("&amp;BBOX=");
1647             msWFSPrintURLAndXMLEncoded(paramsObj->pszBbox);
1648         }
1649         if( paramsObj->pszSrs != NULL )
1650         {
1651             msIO_printf("&amp;SRSNAME=");
1652             msWFSPrintURLAndXMLEncoded(paramsObj->pszSrs);
1653         }
1654         if( paramsObj->pszFilter != NULL )
1655         {
1656             msIO_printf("&amp;FILTER=");
1657             msWFSPrintURLAndXMLEncoded(paramsObj->pszFilter);
1658         }
1659         if( paramsObj->pszPropertyName != NULL)
1660         {
1661             msIO_printf("&amp;PROPERTYNAME=");
1662             msWFSPrintURLAndXMLEncoded(paramsObj->pszPropertyName);
1663         }
1664         if( paramsObj->pszValueReference != NULL)
1665         {
1666             msIO_printf("&amp;VALUEREFERENCE=");
1667             msWFSPrintURLAndXMLEncoded(paramsObj->pszValueReference);
1668         }
1669         if( paramsObj->pszSortBy != NULL )
1670         {
1671             msIO_printf("&amp;SORTBY=");
1672             msWFSPrintURLAndXMLEncoded(paramsObj->pszSortBy);
1673         }
1674         if( paramsObj->nMaxFeatures >= 0 )
1675             msIO_printf("&amp;COUNT=%d", paramsObj->nMaxFeatures);
1676     }
1677     else
1678     {
1679         for(i=0; i<req->NumParams; i++) {
1680             if (req->ParamNames[i] && req->ParamValues[i] &&
1681                 strcasecmp(req->ParamNames[i], "MAP") != 0 &&
1682                 strcasecmp(req->ParamNames[i], "STARTINDEX") != 0 &&
1683                 strcasecmp(req->ParamNames[i], "RESULTTYPE") != 0) {
1684                 if( !bFirstArg )
1685                     msIO_printf("&amp;");
1686                 bFirstArg = MS_FALSE;
1687                 msIO_printf("%s=", req->ParamNames[i]);
1688                 msWFSPrintURLAndXMLEncoded(req->ParamValues[i]);
1689             }
1690         }
1691     }
1692 }
1693 
1694 
msWFSGetFeature_GetTimeStamp(char * timestring,size_t timestringlen)1695 static void msWFSGetFeature_GetTimeStamp(char* timestring, size_t timestringlen)
1696 {
1697     struct tm *now;
1698     time_t tim=time(NULL);
1699 
1700     now=localtime(&tim);
1701 
1702     snprintf(timestring, timestringlen, "%d-%02d-%02dT%02d:%02d:%02d",
1703                 now->tm_year+1900, now->tm_mon+1, now->tm_mday,
1704                 now->tm_hour, now->tm_min, now->tm_sec);
1705 }
1706 
msWFSGetFeature_GMLPreamble(mapObj * map,cgiRequestObj * req,WFSGMLInfo * gmlinfo,wfsParamsObj * paramsObj,OWSGMLVersion outputformat,int iResultTypeHits,int iNumberOfFeatures,int nMatchingFeatures,int maxfeatures,int bHasNextFeatures,int nWFSVersion)1707 static int msWFSGetFeature_GMLPreamble( mapObj *map,
1708                                         cgiRequestObj *req,
1709                                         WFSGMLInfo *gmlinfo,
1710                                         wfsParamsObj *paramsObj,
1711                                         OWSGMLVersion outputformat,
1712                                         int iResultTypeHits,
1713                                         int iNumberOfFeatures,
1714                                         int nMatchingFeatures,
1715                                         int maxfeatures,
1716                                         int bHasNextFeatures,
1717                                         int nWFSVersion )
1718 
1719 {
1720   const char *value;
1721   char       *encoded_version, *encoded_typename, *encoded_schema;
1722   gmlNamespaceListObj *namespaceList=NULL; /* for external application schema support */
1723   char timestring[100];
1724   timestring[0] = '\0';
1725 
1726   namespaceList = msGMLGetNamespaces(&(map->web), "G");
1727   if (namespaceList == NULL) {
1728     msSetError(MS_MISCERR, "Unable to populate namespace list", "msWFSGetFeature_GMLPreamble()");
1729     return MS_FAILURE;
1730   }
1731 
1732   /*
1733   ** Establish script_url.
1734   */
1735 
1736   if ((gmlinfo->script_url=msOWSGetOnlineResource(map,"FO","onlineresource",req)) ==NULL ||
1737       (gmlinfo->script_url_encoded = msEncodeHTMLEntities(gmlinfo->script_url)) == NULL) {
1738     msSetError(MS_WFSERR, "Server URL not found", "msWFSGetFeature()");
1739     msGMLFreeNamespaces(namespaceList);
1740     return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
1741   }
1742 
1743   /*
1744   ** Write encoding.
1745   */
1746   msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
1747 
1748   value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
1749   if(value) gmlinfo->user_namespace_uri = value;
1750   gmlinfo->user_namespace_uri_encoded =
1751     msEncodeHTMLEntities(gmlinfo->user_namespace_uri);
1752 
1753   value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
1754   if(value) gmlinfo->user_namespace_prefix = value;
1755 
1756   if(gmlinfo->user_namespace_prefix != NULL && msIsXMLTagValid(gmlinfo->user_namespace_prefix) == MS_FALSE)
1757     msIO_printf("<!-- WARNING: The value '%s' is not valid XML namespace. -->\n", gmlinfo->user_namespace_prefix);
1758 
1759   value = msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection");
1760   if(value) gmlinfo->collection_name = value;
1761 
1762   encoded_version = msEncodeHTMLEntities( paramsObj->pszVersion );
1763   encoded_typename = msEncodeHTMLEntities( gmlinfo->typename );
1764   encoded_schema = msEncodeHTMLEntities( msOWSGetSchemasLocation(map) );
1765 
1766   if( nWFSVersion >= OWS_2_0_0) {
1767     int nNextStartIndex;
1768     char* tmp;
1769     char* encoded_mime_type;
1770     int bAbleToPrintPreviousOrNext = MS_TRUE;
1771 
1772     tmp = msEncodeUrl( gmlinfo->output_mime_type );
1773     encoded_mime_type = msEncodeHTMLEntities( tmp );
1774     msFree(tmp);
1775 
1776     msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
1777 
1778     if( strcasecmp(paramsObj->pszRequest, "GetFeature") == 0 )
1779       msIO_printf("<wfs:FeatureCollection\n");
1780     else
1781       msIO_printf("<wfs:ValueCollection\n");
1782 
1783     msWFS_NS_printf(gmlinfo->user_namespace_prefix,
1784                     gmlinfo->user_namespace_uri_encoded);
1785     msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1786                     msWFSGetGMLNamespaceURIFromGMLVersion(outputformat));
1787     msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
1788                     MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
1789     msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
1790                     MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
1791     msWFSPrintAdditionalNamespaces(namespaceList);
1792 
1793     msIO_printf("   xsi:schemaLocation=\"%s %sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s "
1794                                         "%s %s%s "
1795                                         "%s %s%s\"\n",
1796                 gmlinfo->user_namespace_uri_encoded,
1797                 gmlinfo->script_url_encoded, encoded_version,
1798                 encoded_typename,
1799                 gmlinfo->output_schema_format,
1800 
1801                 MS_OWSCOMMON_WFS_20_NAMESPACE_URI,
1802                 encoded_schema,
1803                 MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION,
1804 
1805                 msWFSGetGMLNamespaceURIFromGMLVersion(outputformat),
1806                 encoded_schema,
1807                 msWFSGetGMLSchemaLocation(outputformat));
1808 
1809     msIO_printf("   timeStamp=\"%s\" numberMatched=\"", timestring);
1810     if( nMatchingFeatures < 0 )
1811     {
1812         /* If we don't know the exact number, at least return something */
1813         /* equivalent to what we would return with WFS 1.1, otherwise */
1814         /* resultType=hits would not return anything usefull to the client. */
1815         if( iResultTypeHits == MS_TRUE )
1816             msIO_printf("%d", iNumberOfFeatures);
1817         else
1818             msIO_printf("unknown");
1819     }
1820     else
1821         msIO_printf("%d", nMatchingFeatures);
1822     msIO_printf("\" numberReturned=\"%d\"",
1823                 (iResultTypeHits == 1) ? 0 : iNumberOfFeatures);
1824 
1825     /* TODO: in case of a multi-layer GetFeature POST, it is difficult to build a */
1826     /* valid GET KVP GetFeature/GetPropertyValue when some options are specified. So for now just */
1827     /* avoid those problematic cases */
1828     if( req->postrequest != NULL && (paramsObj->bHasPostStoredQuery ||
1829         (strchr(encoded_typename, ',') != NULL &&
1830         (paramsObj->pszFilter != NULL || paramsObj->pszPropertyName != NULL ||
1831          paramsObj->pszSortBy != NULL))) )
1832     {
1833         bAbleToPrintPreviousOrNext = MS_FALSE;
1834     }
1835 
1836     if( bAbleToPrintPreviousOrNext )
1837     {
1838         if( maxfeatures > 0 &&
1839             iResultTypeHits != 1 && paramsObj->nStartIndex > 0 &&
1840             ((nMatchingFeatures < 0  && iNumberOfFeatures > 0) ||
1841              (nMatchingFeatures >= 0 && paramsObj->nStartIndex < nMatchingFeatures)) )
1842         {
1843             int nPrevStartIndex;
1844 
1845             msIO_printf("\n");
1846             msIO_printf("   previous=\"");
1847             msWFSGetFeature_PrintBasePrevNextURI(req,gmlinfo, paramsObj,
1848                                                 encoded_version,
1849                                                 encoded_typename,
1850                                                 encoded_mime_type);
1851 
1852             nPrevStartIndex = paramsObj->nStartIndex - maxfeatures;
1853             if( nPrevStartIndex > 0 )
1854                 msIO_printf("&amp;STARTINDEX=%d", nPrevStartIndex);
1855             msIO_printf("\"");
1856         }
1857 
1858         if( iResultTypeHits != 1 && paramsObj->nStartIndex >= 0 )
1859             nNextStartIndex = paramsObj->nStartIndex;
1860         else
1861             nNextStartIndex = 0;
1862         if( maxfeatures > 0 && (bHasNextFeatures || (nMatchingFeatures > 0 &&
1863             (iResultTypeHits == 1 || iNumberOfFeatures + nNextStartIndex < nMatchingFeatures))) )
1864         {
1865             msIO_printf("\n");
1866             msIO_printf("   next=\"");
1867             msWFSGetFeature_PrintBasePrevNextURI(req,gmlinfo, paramsObj,
1868                                                 encoded_version,
1869                                                 encoded_typename,
1870                                                 encoded_mime_type);
1871 
1872             if( iResultTypeHits != 1 )
1873                 nNextStartIndex += iNumberOfFeatures;
1874 
1875             if( nNextStartIndex > 0 )
1876                 msIO_printf("&amp;STARTINDEX=%d", nNextStartIndex);
1877             msIO_printf("\"");
1878         }
1879     }
1880 
1881     msIO_printf(">\n");
1882 
1883     msFree(encoded_mime_type);
1884   }
1885   /*
1886   ** GML 2.x
1887   */
1888   else if(outputformat == OWS_GML2) { /* use a wfs:FeatureCollection */
1889     msIO_printf("<wfs:FeatureCollection\n");
1890     msWFS_NS_printf(gmlinfo->user_namespace_prefix,
1891                     gmlinfo->user_namespace_uri_encoded);
1892     msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
1893                     MS_OWSCOMMON_WFS_NAMESPACE_URI);
1894     msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1895                     MS_OWSCOMMON_GML_NAMESPACE_URI);
1896     msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
1897                     MS_OWSCOMMON_OGC_NAMESPACE_URI);
1898     msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
1899                     MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
1900     msWFSPrintAdditionalNamespaces(namespaceList);
1901 
1902     /* FIXME ? : the schemaLocation will be only valid for WFS 1.0.0 */
1903     msIO_printf("   xsi:schemaLocation=\"http://www.opengis.net/wfs %s/wfs/%s/WFS-basic.xsd \n"
1904                 "                       %s %sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s\">\n",
1905                 encoded_schema, encoded_version,
1906                 gmlinfo->user_namespace_uri_encoded,
1907                 gmlinfo->script_url_encoded, encoded_version,
1908                 encoded_typename, gmlinfo->output_schema_format);
1909   }
1910 
1911   /*
1912   ** GML 3
1913   */
1914   else if( outputformat == OWS_GML3 ) {
1915     if( nWFSVersion == OWS_1_1_0 ) {
1916       msIO_printf("<wfs:FeatureCollection\n");
1917       msWFS_NS_printf(gmlinfo->user_namespace_prefix,
1918                       gmlinfo->user_namespace_uri_encoded);
1919       msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1920                       MS_OWSCOMMON_GML_NAMESPACE_URI);
1921       msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
1922                       MS_OWSCOMMON_WFS_NAMESPACE_URI);
1923       msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
1924                       MS_OWSCOMMON_OGC_NAMESPACE_URI);
1925       msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
1926                       MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
1927     } else {
1928       msIO_printf("<%s:%s\n"
1929                   "   version=\"1.0.0\"\n",
1930                   gmlinfo->user_namespace_prefix,
1931                   gmlinfo->collection_name);
1932       msWFS_NS_printf(gmlinfo->user_namespace_prefix,
1933                       gmlinfo->user_namespace_uri_encoded);
1934       msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1935                       MS_OWSCOMMON_GML_NAMESPACE_URI);
1936       msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
1937                       MS_OWSCOMMON_OGC_NAMESPACE_URI);
1938       msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
1939                       MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
1940     }
1941 
1942     msWFSPrintAdditionalNamespaces(namespaceList);
1943 
1944     msIO_printf("   xsi:schemaLocation=\"%s %sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s",
1945                 gmlinfo->user_namespace_uri_encoded,
1946                 gmlinfo->script_url_encoded, encoded_version,
1947                 encoded_typename, gmlinfo->output_schema_format);
1948 
1949     if( nWFSVersion == OWS_1_1_0 ) {
1950 
1951       msIO_printf("  %s %s%s",
1952                   MS_OWSCOMMON_WFS_NAMESPACE_URI,
1953                   encoded_schema,
1954                   MS_OWSCOMMON_WFS_11_SCHEMA_LOCATION);
1955 
1956       if (iResultTypeHits == 1) {
1957         msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
1958 
1959         msIO_printf("\" timeStamp=\"%s\" numberOfFeatures=\"%d",
1960                     timestring, iNumberOfFeatures);
1961       }
1962     }
1963     msIO_printf("\">\n");
1964   }
1965 
1966   msFree(encoded_version);
1967   msFree(encoded_schema);
1968   msFree(encoded_typename);
1969 
1970   msGMLFreeNamespaces(namespaceList);
1971 
1972   return MS_SUCCESS;
1973 }
1974 
1975 /*
1976 ** msWFSGetFeature_GMLPostfix()
1977 **
1978 ** Generate the GML file tail closing the collection and cleanup a bit.
1979 */
1980 
msWFSGetFeature_GMLPostfix(mapObj * map,cgiRequestObj * req,WFSGMLInfo * gmlinfo,wfsParamsObj * paramsObj,OWSGMLVersion outputformat,int maxfeatures,int iResultTypeHits,int iNumberOfFeatures,int nWFSVersion)1981 static int msWFSGetFeature_GMLPostfix( mapObj *map,
1982                                        cgiRequestObj *req,
1983                                        WFSGMLInfo *gmlinfo,
1984                                        wfsParamsObj *paramsObj,
1985                                        OWSGMLVersion outputformat,
1986                                        int maxfeatures,
1987                                        int iResultTypeHits,
1988                                        int iNumberOfFeatures,
1989                                        int nWFSVersion )
1990 
1991 {
1992   if (((iNumberOfFeatures==0) || (maxfeatures == 0)) && iResultTypeHits == 0) {
1993 
1994     if( nWFSVersion < OWS_2_0_0 )
1995     {
1996       msIO_printf("   <gml:boundedBy>\n");
1997       if(outputformat == OWS_GML3 || outputformat == OWS_GML32 )
1998         msIO_printf("      <gml:Null>missing</gml:Null>\n");
1999       else
2000         msIO_printf("      <gml:null>missing</gml:null>\n");
2001       msIO_printf("   </gml:boundedBy>\n");
2002     }
2003   }
2004 
2005   if(outputformat == OWS_GML2)
2006     msIO_printf("</wfs:FeatureCollection>\n\n");
2007   else {
2008     if( nWFSVersion >= OWS_1_1_0 )
2009       msIO_printf("</wfs:FeatureCollection>\n\n");
2010     else
2011       msIO_printf("</%s:%s>\n\n", gmlinfo->user_namespace_prefix, gmlinfo->collection_name);
2012   }
2013 
2014   return MS_SUCCESS;
2015 }
2016 
2017 /*
2018 ** msWFSBuildParamList()
2019 */
msWFSBuildParamList(char ** ppszStrList,const char * pszValue,const char * pszSep)2020 static void msWFSBuildParamList(char** ppszStrList, const char* pszValue,
2021                                 const char* pszSep)
2022 {
2023     if (*ppszStrList == NULL)
2024         *ppszStrList = msStrdup(pszValue);
2025     else
2026     {
2027         char* pszTmp = msStrdup(*ppszStrList);
2028         *ppszStrList =
2029             (char *)msSmallRealloc(*ppszStrList,
2030                                     sizeof(char)*
2031                                     (strlen(pszTmp)+
2032                                      strlen(pszSep)+
2033                                      strlen(pszValue)+1));
2034 
2035         sprintf(*ppszStrList,"%s%s%s",pszTmp,pszSep,pszValue);
2036         free(pszTmp);
2037     }
2038 }
2039 
2040 /*
2041 ** msWFSTurnOffAllLayer()
2042 */
msWFSTurnOffAllLayer(mapObj * map)2043 static void msWFSTurnOffAllLayer(mapObj* map)
2044 {
2045     int j;
2046     for(j=0; j<map->numlayers; j++) {
2047       layerObj *lp;
2048       lp = GET_LAYER(map, j);
2049       lp->status = MS_OFF;
2050     }
2051 }
2052 
2053 /*
2054 ** msWFSRunFilter()
2055 */
msWFSRunFilter(mapObj * map,layerObj * lp,const wfsParamsObj * paramsObj,const char * pszFilter,int nWFSVersion)2056 static int msWFSRunFilter(mapObj* map,
2057                           layerObj* lp,
2058                           const wfsParamsObj *paramsObj,
2059                           const char* pszFilter,
2060                           int nWFSVersion)
2061 {
2062     int layerWasOpened;
2063     FilterEncodingNode *psNode = NULL;
2064 
2065     psNode = FLTParseFilterEncoding(pszFilter);
2066 
2067     if (!psNode) {
2068         msSetError(MS_WFSERR,
2069                    "Invalid or Unsupported FILTER in GetFeature : %s",
2070                    "msWFSGetFeature()", pszFilter);
2071         return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2072     }
2073 
2074     /* Starting with WFS 1.1, we need to swap coordinates of BBOX or geometry */
2075     /* parameters in some circumstances */
2076     if( nWFSVersion >= OWS_1_1_0 )
2077     {
2078           int bDefaultSRSNeedsAxisSwapping = MS_FALSE;
2079           char* srs;
2080           msOWSGetEPSGProj(&(map->projection),&(map->web.metadata),"FO",MS_TRUE,&srs);
2081           if (!srs)
2082           {
2083               msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE, &srs);
2084           }
2085           if ( srs && strncasecmp(srs, "EPSG:", 5) == 0 )
2086           {
2087               bDefaultSRSNeedsAxisSwapping = msIsAxisInverted(atoi(srs+5));
2088           }
2089           msFree(srs);
2090           FLTDoAxisSwappingIfNecessary(map, psNode, bDefaultSRSNeedsAxisSwapping);
2091     }
2092 
2093     layerWasOpened = msLayerIsOpen(lp);
2094     if( !layerWasOpened && msLayerOpen(lp) != MS_SUCCESS )
2095     {
2096         FLTFreeFilterEncodingNode( psNode );
2097         return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2098     }
2099 
2100     FLTProcessPropertyIsNull(psNode, map, lp->index);
2101 
2102     /*preparse the filter for gml aliases*/
2103     FLTPreParseFilterForAliasAndGroup(psNode, map, lp->index, "G");
2104 
2105     /* Check that FeatureId filters are consistent with the active layer */
2106     if( FLTCheckFeatureIdFilters(psNode, map, lp->index) == MS_FAILURE)
2107     {
2108         FLTFreeFilterEncodingNode( psNode );
2109         return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2110     }
2111 
2112     /* FIXME?: could probably apply to WFS 1.1 too */
2113     if( nWFSVersion >= OWS_2_0_0 )
2114     {
2115         int nEvaluation;
2116 
2117         if( FLTCheckInvalidOperand(psNode) == MS_FAILURE)
2118         {
2119             FLTFreeFilterEncodingNode( psNode );
2120             return msWFSException(map, "filter", MS_WFS_ERROR_OPERATION_PROCESSING_FAILED, paramsObj->pszVersion);
2121         }
2122 
2123         if( FLTCheckInvalidProperty(psNode, map, lp->index) == MS_FAILURE)
2124         {
2125             FLTFreeFilterEncodingNode( psNode );
2126             return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2127         }
2128 
2129         psNode = FLTSimplify(psNode, &nEvaluation);
2130         if( psNode == NULL )
2131         {
2132             FLTFreeFilterEncodingNode( psNode );
2133             if( nEvaluation == 1 ) {
2134                 /* return full layer */
2135                 return msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
2136             }
2137             else {
2138                 /* return empty result set */
2139                 return MS_SUCCESS;
2140             }
2141         }
2142 
2143     }
2144 
2145     /* run filter.  If no results are found, do not throw exception */
2146     /* this is a null result */
2147     if( FLTApplyFilterToLayer(psNode, map, lp->index) != MS_SUCCESS ) {
2148         errorObj* ms_error = msGetErrorObj();
2149 
2150         if(ms_error->code != MS_NOTFOUND) {
2151             msSetError(MS_WFSERR, "FLTApplyFilterToLayer() failed", "msWFSGetFeature()");
2152             FLTFreeFilterEncodingNode( psNode );
2153             return msWFSException(map, "mapserv", ( nWFSVersion >= OWS_2_0_0 ) ? MS_WFS_ERROR_OPERATION_PROCESSING_FAILED : MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2154         }
2155     }
2156 
2157     FLTFreeFilterEncodingNode( psNode );
2158 
2159     return MS_SUCCESS;
2160 }
2161 
2162 /*
2163 ** msWFSRunBasicGetFeature()
2164 */
msWFSRunBasicGetFeature(mapObj * map,layerObj * lp,const wfsParamsObj * paramsObj,int nWFSVersion)2165 static int msWFSRunBasicGetFeature(mapObj* map,
2166                                    layerObj* lp,
2167                                    const wfsParamsObj *paramsObj,
2168                                    int nWFSVersion)
2169 {
2170     rectObj ext;
2171     int status;
2172 
2173     map->query.type = MS_QUERY_BY_RECT; /* setup the query */
2174     map->query.mode = MS_QUERY_MULTIPLE;
2175     map->query.rect = map->extent;
2176     map->query.layer = lp->index;
2177 
2178     if( FLTLayerSetInvalidRectIfSupported(lp, &(map->query.rect)) )
2179     {
2180         /* do nothing */
2181     }
2182     else if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS) {
2183         char *pszMapSRS=NULL;
2184 
2185         /*if srsName was given for wfs 1.1.0, It is at this point loaded into the
2186         map object and should be used*/
2187         if(!paramsObj->pszSrs)
2188           msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE, &pszMapSRS);
2189 
2190         /* For a single point layer, to avoid numerical precision issues */
2191         /* when reprojection is involved */
2192         ext.minx -= 1e-5;
2193         ext.miny -= 1e-5;
2194         ext.maxx += 1e-5;
2195         ext.maxy += 1e-5;
2196 
2197         if (pszMapSRS != NULL && strncmp(pszMapSRS, "EPSG:", 5) == 0) {
2198 
2199             if( nWFSVersion >= OWS_1_1_0 )
2200                 status = msLoadProjectionStringEPSG(&(map->projection), pszMapSRS);
2201             else
2202                 status = msLoadProjectionString(&(map->projection), pszMapSRS);
2203 
2204             if (status != 0) {
2205                 msSetError(MS_WFSERR, "msLoadProjectionString() failed: %s", "msWFSGetFeature()", pszMapSRS);
2206                 msFree(pszMapSRS);
2207                 return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2208             }
2209         }
2210         msFree(pszMapSRS);
2211 
2212         /*make sure that the layer projection is loaded.
2213             It could come from a ows/wfs_srs metadata*/
2214         if (lp->projection.numargs == 0) {
2215             char *pszLayerSRS;
2216             msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE, &pszLayerSRS);
2217             if (pszLayerSRS) {
2218                 if (strncmp(pszLayerSRS, "EPSG:", 5) == 0) {
2219                     if( nWFSVersion >= OWS_1_1_0 )
2220                         msLoadProjectionStringEPSG(&(lp->projection), pszLayerSRS);
2221                     else
2222                         msLoadProjectionString(&(lp->projection), pszLayerSRS);
2223                 }
2224             }
2225             msFree(pszLayerSRS);
2226         }
2227 
2228         if (msProjectionsDiffer(&map->projection, &lp->projection) == MS_TRUE) {
2229             msProjectRect(&lp->projection, &map->projection, &(ext));
2230         }
2231         map->query.rect = ext;
2232     }
2233 
2234     if(msQueryByRect(map) != MS_SUCCESS) {
2235         errorObj   *ms_error;
2236         ms_error = msGetErrorObj();
2237 
2238         if(ms_error->code != MS_NOTFOUND) {
2239             msSetError(MS_WFSERR, "ms_error->code not found", "msWFSGetFeature()");
2240             return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2241         }
2242     }
2243 
2244     return MS_SUCCESS;
2245 }
2246 
2247 
2248 /*
2249 ** msWFSRetrieveFeatures()
2250 */
msWFSRetrieveFeatures(mapObj * map,owsRequestObj * ows_request,const wfsParamsObj * paramsObj,const WFSGMLInfo * gmlinfo,const char * pszFilter,int bBBOXSet,const char * pszBBOXSRS,rectObj bbox,const char * pszFeatureId,char ** layers,int numlayers,int maxfeatures,int nWFSVersion,int * pnOutTotalFeatures,int * pnHasNext)2251 static int msWFSRetrieveFeatures(mapObj* map,
2252                                  owsRequestObj* ows_request,
2253                               const wfsParamsObj *paramsObj,
2254                               const WFSGMLInfo* gmlinfo,
2255                               const char* pszFilter,
2256                               int bBBOXSet,
2257                               const char* pszBBOXSRS,
2258                               rectObj bbox,
2259                               const char* pszFeatureId,
2260                               char **layers,
2261                               int numlayers,
2262                               int maxfeatures,
2263                               int nWFSVersion,
2264                               int* pnOutTotalFeatures,
2265                               int* pnHasNext)
2266 {
2267   int i, j;
2268   int iNumberOfFeatures = 0;
2269 
2270   if (pszFilter && strlen(pszFilter) > 0) {
2271     int nFilters;
2272     char **paszFilter = NULL;
2273 
2274     /* -------------------------------------------------------------------- */
2275     /*      Validate the parameters. When a FILTER parameter is given,      */
2276     /*      It needs the TYPENAME parameter for the layers. Also Filter     */
2277     /*      is Mutually exclusive with FEATUREID and BBOX (see wfs specs    */
2278     /*      1.0 section 13.7.3 on GetFeature)                               */
2279     /*                                                                      */
2280     /* -------------------------------------------------------------------- */
2281 
2282     /* WFS 2.0 */
2283     if (nWFSVersion >= OWS_2_0_0 &&
2284         paramsObj->pszFilterLanguage != NULL &&
2285         strcasecmp(paramsObj->pszFilterLanguage, "urn:ogc:def:query Language:OGC-FES:Filter") != 0) {
2286       msSetError(MS_WFSERR,
2287                  "Unhandled value for FILTER_LANGUAGE parameter. Only \"urn:ogc:def:query Language:OGC-FES:Filter\" accepted.",
2288                  "msWFSGetFeature()");
2289       return msWFSException(map, "filter_language", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2290     }
2291 
2292     if (gmlinfo->typename == NULL || strlen(gmlinfo->typename) <= 0 || layers == NULL || numlayers <= 0) {
2293       msSetError(MS_WFSERR,
2294                  "Required %s parameter missing for GetFeature with a FILTER parameter.",
2295                  "msWFSGetFeature()",
2296                  (nWFSVersion >= OWS_2_0_0 ) ? "TYPENAMES": "TYPENAME" );
2297       return msWFSException(map, (nWFSVersion >= OWS_2_0_0 ) ? "typenames": "typename",
2298                             MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
2299     }
2300 
2301     if (bBBOXSet) {
2302       msSetError(MS_WFSERR,
2303                  "BBOX parameter and FILTER parameter are mutually exclusive in GetFeature.",
2304                  "msWFSGetFeature()");
2305       return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2306     }
2307 
2308     if (pszFeatureId != NULL) {
2309       msSetError(MS_WFSERR,
2310                  "%s parameter and FILTER parameter are mutually exclusive in GetFeature.",
2311                  "msWFSGetFeature()",
2312                  (nWFSVersion < OWS_2_0_0 ) ? "FEATUREID" : "RESOURCEID" );
2313       return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2314     }
2315 
2316     if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) == MS_FAILURE)
2317         return msWFSException(map, "srsname", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2318 
2319     /* -------------------------------------------------------------------- */
2320     /*      Parse the Filter parameter. If there are several Filter         */
2321     /*      parameters, each Filter is inside a parantheses. Eg :           */
2322     /*      FILTER=(<Filter><Within><PropertyName>                          */
2323     /*      INWATERA_1M/WKB_GEOM|INWATERA_1M/WKB_GEOM                       */
2324     /*      <PropertyName><gml:Box><gml:coordinates>10,10 20,20</gml:coordinates>*/
2325     /*      </gml:Box></Within></Filter>)(<Filter><Within><PropertyName>    */
2326     /*      INWATERA_1M/WKB_GEOM<PropertyName><gml:Box><gml:coordinates>10,10*/
2327     /*      20,20</gml:coordinates></gml:Box></Within></Filter>)            */
2328     /* -------------------------------------------------------------------- */
2329     nFilters = 0;
2330     if (strlen(pszFilter) > 0 && pszFilter[0] == '(') {
2331       paszFilter = FLTSplitFilters(pszFilter, &nFilters);
2332 
2333       if ( paszFilter && nFilters > 0 && numlayers != nFilters ) {
2334         msFreeCharArray(paszFilter, nFilters);
2335       }
2336     } else if (numlayers == 1) {
2337       nFilters=1;
2338       paszFilter = (char **)msSmallMalloc(sizeof(char *)*nFilters);
2339       paszFilter[0] = msStrdup(pszFilter);
2340     }
2341 
2342     if (numlayers != nFilters) {
2343       msSetError(MS_WFSERR, "Wrong number of filter elements, one filter must be specified for each feature type listed in the %s parameter.",
2344                  "msWFSGetFeature()",
2345                  (nWFSVersion >= OWS_2_0_0 ) ? "TYPENAMES": "TYPENAME" );
2346       return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2347     }
2348 
2349     /* -------------------------------------------------------------------- */
2350     /*      run through the filters and build the class expressions.        */
2351     /* -------------------------------------------------------------------- */
2352     for (i=0; i<nFilters; i++) {
2353       int status;
2354       layerObj* lp;
2355 
2356       lp = msWFSGetLayerByName(map, ows_request, layers[i]);
2357 
2358       /* Special value set when parsing XML Post when there is a mix of */
2359       /* Query with and without Filter */
2360       if( strcmp(paszFilter[i], "!") == 0 )
2361         status = msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
2362       else
2363         status = msWFSRunFilter(map, lp, paramsObj, paszFilter[i], nWFSVersion);
2364 
2365       if( status != MS_SUCCESS )
2366       {
2367           msFreeCharArray(paszFilter, nFilters);
2368           return status;
2369       }
2370 
2371       /* Decrement the total maxfeatures */
2372       if( map->query.maxfeatures >= 0 )
2373       {
2374           if( lp->resultcache && lp->resultcache->numresults > 0 )
2375           {
2376             map->query.maxfeatures -= lp->resultcache->numresults;
2377             if( map->query.maxfeatures <= 0 )
2378                 break;
2379           }
2380       }
2381     }
2382 
2383     msFreeCharArray(paszFilter, nFilters);
2384   }/* end if filter set */
2385 
2386 
2387   if (pszFeatureId != NULL) {
2388     char **tokens = NULL;
2389     int nTokens = 0, j=0, k=0;
2390     FilterEncodingNode *psNode = NULL;
2391     char **aFIDLayers = NULL;
2392     char **aFIDValues = NULL;
2393     int iFIDLayers = 0;
2394 
2395     /* Keep only selected layers, set to OFF by default. */
2396     msWFSTurnOffAllLayer(map);
2397 
2398     /*featureid can be a list INWATERA_1M.1234, INWATERA_1M.1235
2399     We will keep all the feature id from the same layer together
2400     so that an OR would be applied if several of them are present
2401     */
2402     tokens = msStringSplit(pszFeatureId, ',', &nTokens);
2403     iFIDLayers = 0;
2404     if (tokens && nTokens >=1) {
2405       aFIDLayers = (char **)msSmallMalloc(sizeof(char *)*nTokens);
2406       aFIDValues = (char **)msSmallMalloc(sizeof(char *)*nTokens);
2407       for (j=0; j<nTokens; j++) {
2408         aFIDLayers[j] = NULL;
2409         aFIDValues[j] = NULL;
2410       }
2411       for (j=0; j<nTokens; j++) {
2412         const char* pszLastDot = strrchr(tokens[j], '.');
2413         if (pszLastDot != NULL) {
2414           char* pszLayerInFID = msStrdup(tokens[j]);
2415           pszLayerInFID[pszLastDot - tokens[j]] = '\0';
2416           /* Find if the layer is already requested */
2417           for (k=0; k<iFIDLayers; k++) {
2418             if (strcasecmp(aFIDLayers[k], pszLayerInFID) == 0)
2419               break;
2420           }
2421           /* If not, add it to the list of requested layers */
2422           if (k == iFIDLayers) {
2423             aFIDLayers[iFIDLayers] = msStrdup(pszLayerInFID);
2424             iFIDLayers++;
2425           }
2426           /* Add the id to the list of search identifiers of the layer */
2427           if (aFIDValues[k] != NULL)
2428             aFIDValues[k] = msStringConcatenate(aFIDValues[k], ",");
2429           aFIDValues[k] = msStringConcatenate( aFIDValues[k], pszLastDot + 1);
2430           msFree(pszLayerInFID);
2431         } else {
2432           /* In WFS 20, an unknown/invalid feature id shouldn't trigger an */
2433           /* exception. Tested by CITE */
2434           if( nWFSVersion < OWS_2_0_0 ) {
2435             msSetError(MS_WFSERR,
2436                        "Invalid FeatureId in GetFeature. Expecting layername.value : %s",
2437                        "msWFSGetFeature()", tokens[j]);
2438             if (tokens)
2439               msFreeCharArray(tokens, nTokens);
2440             msFreeCharArray(aFIDLayers, iFIDLayers);
2441             msFreeCharArray(aFIDValues, iFIDLayers);
2442             return msWFSException(map, "featureid", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2443           }
2444         }
2445       }
2446     }
2447     if (tokens)
2448       msFreeCharArray(tokens, nTokens);
2449 
2450     /*turn on the layers and make sure projections are set properly*/
2451     for (j=0; j< iFIDLayers; j++) {
2452       layerObj *lp;
2453       lp = msWFSGetLayerByName(map, ows_request, aFIDLayers[j]);
2454       if( lp == NULL ) {
2455         msSetError(MS_WFSERR,
2456                    "Invalid typename given with FeatureId in GetFeature : %s. A layer might be disabled for \
2457 this request. Check wfs/ows_enable_request settings.", "msWFSGetFeature()",
2458                    aFIDLayers[j]);
2459 
2460         msFreeCharArray(aFIDLayers, iFIDLayers);
2461         msFreeCharArray(aFIDValues, iFIDLayers);
2462         return msWFSException(map, "featureid", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2463       }
2464 
2465       lp->status = MS_ON;
2466     }
2467 
2468     if( map->query.only_cache_result_count == MS_FALSE )
2469         msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, FALSE, NULL, NULL);
2470 
2471     if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) == MS_FAILURE)
2472         return msWFSException(map, "srsname", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2473 
2474     for (j=0; j< iFIDLayers; j++) {
2475       layerObj *lp;
2476       lp = msWFSGetLayerByName(map, ows_request, aFIDLayers[j]);
2477       if (lp->template == NULL) {
2478         /* Force setting a template to enable query. */
2479         lp->template = msStrdup("ttt.html");
2480       }
2481       psNode = FLTCreateFeatureIdFilterEncoding(aFIDValues[j]);
2482 
2483       if( FLTApplyFilterToLayer(psNode, map, lp->index) != MS_SUCCESS ) {
2484         msSetError(MS_WFSERR, "FLTApplyFilterToLayer() failed", "msWFSGetFeature");
2485         FLTFreeFilterEncodingNode( psNode );
2486         msFreeCharArray(aFIDLayers, iFIDLayers);
2487         msFreeCharArray(aFIDValues, iFIDLayers);
2488         return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2489       }
2490 
2491       FLTFreeFilterEncodingNode( psNode );
2492       psNode = NULL;
2493 
2494       /* Decrement the total maxfeatures */
2495       if( map->query.maxfeatures >= 0 )
2496       {
2497           if( lp->resultcache && lp->resultcache->numresults > 0 )
2498           {
2499             map->query.maxfeatures -= lp->resultcache->numresults;
2500             if( map->query.maxfeatures <= 0 )
2501                 break;
2502           }
2503       }
2504     }
2505 
2506     msFreeCharArray(aFIDLayers, iFIDLayers);
2507     msFreeCharArray(aFIDValues, iFIDLayers);
2508   }
2509 
2510   /*
2511   ** Perform Query (only BBOX for now)
2512   */
2513   /* __TODO__ Using a rectangle query may not be the most efficient way */
2514   /* to do things here. */
2515   if (pszFilter == NULL && pszFeatureId == NULL) {
2516 
2517     /* Apply the requested SRS */
2518     if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) == MS_FAILURE)
2519         return msWFSException(map, "srsname", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2520 
2521     if (!bBBOXSet) {
2522       for(j=0; j<map->numlayers; j++) {
2523         layerObj *lp;
2524         lp = GET_LAYER(map, j);
2525         if (lp->status == MS_ON) {
2526           int status = msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
2527           if( status != MS_SUCCESS )
2528               return status;
2529         }
2530       }
2531     } else {
2532 
2533       char* sBBoxSrs = NULL;
2534 
2535       if( pszBBOXSRS != NULL )
2536           sBBoxSrs = msStrdup(pszBBOXSRS);
2537 
2538       /* On WFS 2.0.0, if the BBOX has no explicit SRS, use the map SRS */
2539       /* Should likely be used for WFS 1.1.0 too, but don't want to cause */
2540       /* issue at that point, since it can influence axis ordering */
2541       if( nWFSVersion >= OWS_2_0_0 && sBBoxSrs == NULL )
2542       {
2543           projectionObj sProjTmp;
2544 
2545           msInitProjection(&sProjTmp);
2546           msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
2547           msOWSGetEPSGProj(&sProjTmp,&(map->web.metadata),"FO",MS_TRUE, &sBBoxSrs);
2548           msFreeProjection(&sProjTmp);
2549       }
2550 
2551       if (sBBoxSrs) {
2552         int status;
2553         projectionObj sProjTmp;
2554 
2555         msInitProjection(&sProjTmp);
2556         msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
2557         /*do the axis order for now. It is unclear if the bbox are expected
2558           ro respect the axis oder defined in the projectsion #3296*/
2559 
2560         if(nWFSVersion >= OWS_1_1_0) {
2561           if ((status=msLoadProjectionStringEPSG(&sProjTmp, sBBoxSrs)) == 0) {
2562             msAxisNormalizePoints( &sProjTmp, 1, &bbox.minx, &bbox.miny );
2563             msAxisNormalizePoints( &sProjTmp, 1, &bbox.maxx, &bbox.maxy );
2564           }
2565         } else
2566           status = msLoadProjectionString(&sProjTmp, sBBoxSrs);
2567 
2568         if (status == 0 &&  map->projection.numargs > 0)
2569           msProjectRect(&sProjTmp, &map->projection, &bbox);
2570         msFreeProjection(&sProjTmp);
2571 
2572         msFree(sBBoxSrs);
2573         sBBoxSrs = NULL;
2574       }
2575       map->query.type = MS_QUERY_BY_RECT; /* setup the query */
2576       map->query.mode = MS_QUERY_MULTIPLE;
2577       map->query.rect = bbox;
2578 
2579       /*
2580         Retaining this block of code in case we want to setup a rendering path through
2581         the query code at some point.
2582       */
2583       /* if(map->outputformat && MS_DRIVER_MVT(map->outputformat)) {
2584         const char *mvt_buffer = msGetOutputFormatOption(map->outputformat, "EDGE_BUFFER", "10");
2585         int buffer = MS_ABS(atoi(mvt_buffer));
2586         double res = (bbox.maxx - bbox.minx)/(double)map->width;
2587         bbox.minx -= buffer * res;
2588         bbox.maxx += buffer * res;
2589         res = (bbox.maxy - bbox.miny)/(double)map->height;
2590         bbox.miny -= buffer * res;
2591         bbox.maxy += buffer * res;
2592         map->width += buffer*2;
2593         map->height += buffer*2;
2594         msCalculateScale(bbox,map->units,map->width,map->height, map->resolution, &map->scaledenom);
2595         map->query.rect = bbox;
2596       } */
2597 
2598       if(msQueryByRect(map) != MS_SUCCESS) {
2599         errorObj   *ms_error;
2600         ms_error = msGetErrorObj();
2601 
2602         if(ms_error->code != MS_NOTFOUND) {
2603           msSetError(MS_WFSERR, "ms_error->code not found", "msWFSGetFeature()");
2604           return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
2605         }
2606       }
2607     }
2608   }
2609 
2610   /* Count total number of features retrieved */
2611   for(j=0; j<map->numlayers; j++) {
2612     if (GET_LAYER(map, j)->resultcache && GET_LAYER(map, j)->resultcache->numresults > 0) {
2613       iNumberOfFeatures += GET_LAYER(map, j)->resultcache->numresults;
2614     }
2615   }
2616 
2617   /* If maxfeatures is set, we have actually asked for 1 more feature than asked */
2618   /* to be able to know if there are next features. So it is now time to */
2619   /* remove this extra unasked feature from the result cache */
2620   if( maxfeatures >= 0 && iNumberOfFeatures == maxfeatures + 1 )
2621   {
2622       int lastJ = -1;
2623       for(j=0; j<map->numlayers; j++) {
2624           if (GET_LAYER(map, j)->resultcache && GET_LAYER(map, j)->resultcache->numresults > 0) {
2625               lastJ = j;
2626           }
2627       }
2628       GET_LAYER(map, lastJ)->resultcache->numresults --;
2629       GET_LAYER(map, lastJ)->resultcache->bounds = GET_LAYER(map, lastJ)->resultcache->previousBounds;
2630       iNumberOfFeatures --;
2631       if( pnHasNext )
2632           *pnHasNext = MS_TRUE;
2633   }
2634   else if( pnHasNext )
2635       *pnHasNext = MS_FALSE;
2636 
2637   *pnOutTotalFeatures = iNumberOfFeatures;
2638 
2639   return MS_SUCCESS;
2640 }
2641 
2642 /*
2643 ** msWFSParsePropertyNameOrSortBy()
2644 */
msWFSParsePropertyNameOrSortBy(const char * pszPropertyName,int numlayers)2645 static char** msWFSParsePropertyNameOrSortBy(const char* pszPropertyName,
2646                                              int numlayers)
2647 {
2648     char** tokens = NULL;
2649     int nPropertyNames = 0;
2650     char** papszPropertyName = NULL;
2651     int i;
2652 
2653     if( pszPropertyName == NULL )
2654     {
2655         papszPropertyName = (char **)msSmallMalloc(sizeof(char *)*numlayers);
2656         for (i=0; i<numlayers; i++) {
2657             papszPropertyName[i] = msStrdup("!");
2658         }
2659         return papszPropertyName;
2660     }
2661 
2662     if (!(pszPropertyName[0] == '(' || numlayers == 1))
2663     {
2664         return NULL;
2665     }
2666 
2667     if (numlayers == 1 && pszPropertyName[0] != '(') {
2668         /* Accept PROPERTYNAME without () when there is a single TYPENAME */
2669         char* pszTmpPropertyName = msSmallMalloc(1+strlen(pszPropertyName)+1+1);
2670         sprintf(pszTmpPropertyName, "(%s)", pszPropertyName);
2671         tokens = msStringSplit(pszTmpPropertyName+1, '(', &nPropertyNames);
2672         free(pszTmpPropertyName);
2673     } else
2674         tokens = msStringSplit(pszPropertyName+1, '(', &nPropertyNames);
2675 
2676     /*expecting to always have a list of property names equal to
2677         the number of layers(typename)*/
2678     if (nPropertyNames != numlayers) {
2679         msFreeCharArray(tokens, nPropertyNames);
2680         return NULL;
2681     }
2682 
2683     papszPropertyName = (char **)msSmallMalloc(sizeof(char *)*nPropertyNames);
2684     for (i=0; i<nPropertyNames; i++) {
2685         if (strlen(tokens[i]) > 0) {
2686             papszPropertyName[i] = msStrdup(tokens[i]);
2687             /* remove trailing ) */
2688             papszPropertyName[i][strlen(papszPropertyName[i])-1] = '\0';
2689         }
2690         else {
2691             for ( i--; i>=0; i-- )
2692                 msFree(papszPropertyName[i]);
2693             msFree(papszPropertyName);
2694             msFreeCharArray(tokens, nPropertyNames);
2695             return NULL;
2696         }
2697     }
2698 
2699     msFreeCharArray(tokens, nPropertyNames);
2700 
2701     return papszPropertyName;
2702 }
2703 
msWFSInitGMLInfo(WFSGMLInfo * pgmlinfo)2704 static void msWFSInitGMLInfo(WFSGMLInfo* pgmlinfo)
2705 {
2706   memset( pgmlinfo, 0, sizeof(WFSGMLInfo) );
2707   pgmlinfo->user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
2708   pgmlinfo->user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
2709   pgmlinfo->collection_name = OWS_WFS_FEATURE_COLLECTION_NAME;
2710   pgmlinfo->typename = "";
2711   pgmlinfo->output_mime_type = "text/xml";
2712   pgmlinfo->output_schema_format = "XMLSCHEMA";
2713 }
2714 
msWFSCleanupGMLInfo(WFSGMLInfo * pgmlinfo)2715 static void msWFSCleanupGMLInfo(WFSGMLInfo* pgmlinfo)
2716 {
2717   free(pgmlinfo->script_url);
2718   free(pgmlinfo->script_url_encoded);
2719   msFree(pgmlinfo->user_namespace_uri_encoded);
2720 }
2721 
msWFSGetGMLOutputFormat(mapObj * map,wfsParamsObj * paramsObj,WFSGMLInfo * pgmlinfo,int nWFSVersion)2722 static int msWFSGetGMLOutputFormat(mapObj *map, wfsParamsObj *paramsObj,
2723                                     WFSGMLInfo* pgmlinfo,
2724                                     int nWFSVersion)
2725 {
2726   OWSGMLVersion outputformat = OWS_GML2;
2727 
2728   /* Validate outputformat */
2729   if (paramsObj->pszOutputFormat) {
2730     /* We support GML2, GML3, GML3.2 for now. */
2731     if(strcasecmp(paramsObj->pszOutputFormat, "GML2") == 0 ||
2732        strcasecmp(paramsObj->pszOutputFormat, "text/xml; subtype=gml/2.1.2") == 0) {
2733       outputformat = OWS_GML2;
2734       pgmlinfo->output_schema_format = "XMLSCHEMA";
2735     /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE WFS 2.0 */
2736       pgmlinfo->output_mime_type = "text/xml; subtype=gml/2.1.2";
2737     } else if(strcasecmp(paramsObj->pszOutputFormat, "GML3") == 0 ||
2738               strcasecmp(paramsObj->pszOutputFormat, "text/xml; subtype=gml/3.1.1") == 0) {
2739       outputformat = OWS_GML3;
2740       pgmlinfo->output_schema_format = "SFE_XMLSCHEMA";
2741       /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE WFS 2.0 */
2742       pgmlinfo->output_mime_type = "text/xml; subtype=gml/3.1.1";
2743     } else if(strcasecmp(paramsObj->pszOutputFormat, "GML32") == 0 ||
2744               strcasecmp(paramsObj->pszOutputFormat, "text/xml; subtype=gml/3.2.1") == 0 ||
2745               strcasecmp(paramsObj->pszOutputFormat, "text/xml; subtype=\"gml/3.2.1\"") == 0 ||
2746               strcasecmp(paramsObj->pszOutputFormat, "application/gml+xml; version=3.2") == 0) {
2747       outputformat = OWS_GML32;
2748       pgmlinfo->output_schema_format = "application%2Fgml%2Bxml%3B%20version%3D3.2";
2749       pgmlinfo->output_mime_type = "text/xml; subtype=\"gml/3.2.1\"";
2750     } else {
2751       return -1;
2752     }
2753 
2754     /* If OUTPUTFORMAT not set, default to gml */
2755   } else {
2756     /*set the output format using the version if available*/
2757     if( nWFSVersion == OWS_1_1_0 ) {
2758       outputformat = OWS_GML3;
2759       pgmlinfo->output_schema_format = "text/xml;%20subtype=gml/3.1.1";
2760       /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE WFS 2.0 */
2761       pgmlinfo->output_mime_type = "text/xml; subtype=gml/3.1.1";
2762     }
2763     else if( nWFSVersion >= OWS_2_0_0 ) {
2764       outputformat = OWS_GML32;
2765       pgmlinfo->output_schema_format = "application%2Fgml%2Bxml%3B%20version%3D3.2";
2766       pgmlinfo->output_mime_type = "text/xml; subtype=\"gml/3.2.1\"";
2767     }
2768   }
2769 
2770   if( nWFSVersion == OWS_1_0_0 ) {
2771     pgmlinfo->output_mime_type = "text/xml";
2772   }
2773 
2774   return outputformat;
2775 }
2776 
msWFSGetOtherOutputFormat(mapObj * map,wfsParamsObj * paramsObj)2777 static outputFormatObj* msWFSGetOtherOutputFormat(mapObj *map, wfsParamsObj *paramsObj)
2778 {
2779   const char *format_list;
2780   hashTableObj *md;
2781   outputFormatObj* psFormat = NULL;
2782   int j;
2783 
2784   /* validate selected format against all selected layers. */
2785   for(j=0; j < map->numlayers; j++) {
2786     layerObj *lp;
2787 
2788     lp = GET_LAYER(map, j);
2789 
2790     if( lp->status != MS_ON )
2791         continue;
2792 
2793     md = &(lp->metadata);
2794     format_list = msOWSLookupMetadata(md, "F","getfeature_formatlist");
2795     if( format_list == NULL ) {
2796         md = &(map->web.metadata);
2797         format_list = msOWSLookupMetadata(md, "F","getfeature_formatlist");
2798     }
2799     if (format_list) {
2800         psFormat = msOwsIsOutputFormatValid(
2801                     map, paramsObj->pszOutputFormat, md,
2802                     "F", "getfeature_formatlist");
2803     }
2804     if (psFormat == NULL) {
2805         msSetError(MS_WFSERR,
2806                     "'%s' is not a permitted output format for layer '%s', review wfs_getfeature_formatlist setting.",
2807                     "msWFSGetFeature()",
2808                     paramsObj->pszOutputFormat,
2809                     lp->name );
2810         return NULL;
2811     }
2812 
2813     if( psFormat->imagemode != MS_IMAGEMODE_FEATURE ) {
2814         msSetError(MS_WFSERR,
2815                     "OUTPUTFORMAT '%s' does not have IMAGEMODE FEATURE, and is not permitted for WFS output.",
2816                     "msWFSGetFeature()",
2817                     paramsObj->pszOutputFormat );
2818         return NULL;
2819     }
2820   }
2821 
2822   return psFormat;
2823 }
2824 
msWFSAnalyzeStartIndexAndFeatureCount(mapObj * map,const wfsParamsObj * paramsObj,int bIsHits,int * pmaxfeatures,int * pstartindex)2825 static void msWFSAnalyzeStartIndexAndFeatureCount(mapObj *map, const wfsParamsObj *paramsObj,
2826                                                   int bIsHits,
2827                                                   int *pmaxfeatures, int* pstartindex)
2828 {
2829   const char *tmpmaxfeatures = NULL;
2830   int   maxfeatures=-1,startindex=-1;
2831   int j;
2832 
2833   int nQueriedLayers=0;
2834   layerObj *lpQueried=NULL;
2835 
2836   tmpmaxfeatures = msOWSLookupMetadata(&(map->web.metadata), "FO", "maxfeatures");
2837   if (tmpmaxfeatures)
2838     maxfeatures = atoi(tmpmaxfeatures);
2839 
2840   if( bIsHits )
2841   {
2842     const char* ignoreMaxFeaturesForHits =
2843         msOWSLookupMetadata(&(map->web.metadata), "FO", "maxfeatures_ignore_for_resulttype_hits");
2844 
2845     /* For PostGIS where we have an efficient implementation of hits, default to true */
2846     if( ignoreMaxFeaturesForHits == NULL )
2847     {
2848         int bHasEfficientHits = MS_TRUE;
2849         for(j=0; j<map->numlayers; j++) {
2850             layerObj *lp;
2851             lp = GET_LAYER(map, j);
2852             if (lp->status == MS_ON) {
2853                 if( lp->connectiontype != MS_POSTGIS )
2854                 {
2855                     bHasEfficientHits = MS_FALSE;
2856                     break;
2857                 }
2858             }
2859         }
2860 
2861         if (bHasEfficientHits )
2862             ignoreMaxFeaturesForHits = "true";
2863     }
2864 
2865     if( ignoreMaxFeaturesForHits != NULL && strcasecmp(ignoreMaxFeaturesForHits, "true") == 0 )
2866         maxfeatures = -1;
2867   }
2868 
2869   if (paramsObj->nMaxFeatures >= 0) {
2870     if (maxfeatures < 0 || (maxfeatures > 0 && paramsObj->nMaxFeatures < maxfeatures))
2871       maxfeatures = paramsObj->nMaxFeatures;
2872   }
2873 
2874   nQueriedLayers=0;
2875   for(j=0; j<map->numlayers; j++) {
2876     layerObj *lp;
2877     lp = GET_LAYER(map, j);
2878     if (lp->status == MS_ON) {
2879       /* No reason to handle tolerances for WFS GetFeature */
2880       lp->tolerance = 0;
2881       lpQueried = GET_LAYER(map, j);
2882       nQueriedLayers++;
2883     }
2884   }
2885 
2886   /* WFS convention is that STARTINDEX=0 means the first feature, whereas */
2887   /* in MapServer internals we used another interpretation with STARTINDEX=1 */
2888   /* being the first index. This was before that the SWG WFS clarified things */
2889   /* hence the mess. Ultimately we should likely fix our internals to avoid */
2890   /* possible confusion */
2891   if (paramsObj->nStartIndex >= 0) {
2892     startindex = 1 + paramsObj->nStartIndex;
2893     map->query.startindex = startindex;
2894   }
2895 
2896 
2897   /* maxfeatures set */
2898   if (maxfeatures >= 0) {
2899     int extrafeature = 1;
2900     for(j=0; j<map->numlayers; j++) {
2901       layerObj *lp;
2902       lp = GET_LAYER(map, j);
2903       if (lp->status == MS_ON) {
2904         /*over-ride the value only if it is unset or wfs maxfeatures is
2905           lower that what is currently set*/
2906         if (lp->maxfeatures <=0 || (lp->maxfeatures > 0 && maxfeatures < lp->maxfeatures))
2907         {
2908           /* TODO: We don't handle DESC sorting, as it would mean to be */
2909           /* able to evict the first property, which mapquery cannot handle */
2910           /* This would have impact on the resultcache.bounds */
2911           if( lp->sortBy.nProperties > 0 )
2912           {
2913             int k;
2914             int countDesc = 1;
2915             for(k=0;k<lp->sortBy.nProperties;k++)
2916             {
2917                 if( lp->sortBy.properties[k].sortOrder == SORT_DESC )
2918                     countDesc ++;
2919             }
2920             if( countDesc > 0 )
2921                 extrafeature = 0;
2922           }
2923 
2924           /* + 1 to be able to detect if there are more features for a next request */
2925           lp->maxfeatures = maxfeatures + extrafeature;
2926         }
2927       }
2928     }
2929     /* + 1 to be able to detect if there are more features for a next request */
2930     map->query.maxfeatures = maxfeatures + extrafeature;
2931   }
2932 
2933   /* startindex set */
2934   if (startindex > 0 && nQueriedLayers > 1) {
2935     for(j=0; j<map->numlayers; j++) {
2936       layerObj *lp;
2937       lp = GET_LAYER(map, j);
2938       if (lp->status == MS_ON) {
2939         msLayerEnablePaging(lp, MS_FALSE);
2940       }
2941     }
2942   } else if (startindex > 0 && lpQueried) {
2943     lpQueried->startindex = startindex;
2944   }
2945 
2946   if( pmaxfeatures )
2947     *pmaxfeatures = maxfeatures;
2948   if( pstartindex )
2949     *pstartindex = startindex;
2950 }
2951 
msWFSAnalyzeBBOX(mapObj * map,wfsParamsObj * paramsObj,rectObj * pbbox,char ** pszBBoxSRS)2952 static int msWFSAnalyzeBBOX(mapObj *map, wfsParamsObj *paramsObj,
2953                             rectObj* pbbox, char** pszBBoxSRS)
2954 {
2955   /* Default filter is map extents */
2956   *pbbox = map->extent;
2957 
2958   if (paramsObj->pszBbox) {
2959     char **tokens;
2960     int n;
2961 
2962     tokens = msStringSplit(paramsObj->pszBbox, ',', &n);
2963     if (tokens==NULL || (n != 4 && n !=5)) {
2964       msSetError(MS_WFSERR, "Wrong number of arguments for BBOX.", "msWFSGetFeature()");
2965       msFreeCharArray(tokens, n);
2966       return msWFSException(map, "bbox", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
2967     }
2968     pbbox->minx = atof(tokens[0]);
2969     pbbox->miny = atof(tokens[1]);
2970     pbbox->maxx = atof(tokens[2]);
2971     pbbox->maxy = atof(tokens[3]);
2972     /*5th aregument is assumed to be projection*/
2973     if (n == 5)
2974       *pszBBoxSRS = msStrdup(tokens[4]);
2975 
2976     msFreeCharArray(tokens, n);
2977   }
2978   return 0;
2979 }
2980 
msWFSComputeMatchingFeatures(mapObj * map,owsRequestObj * ows_request,wfsParamsObj * paramsObj,int iNumberOfFeatures,int maxfeatures,WFSGMLInfo * pgmlinfo,rectObj bbox,const char * sBBoxSrs,char ** layers,int numlayers,int nWFSVersion)2981 static int msWFSComputeMatchingFeatures(mapObj *map,
2982                                         owsRequestObj *ows_request,
2983                                         wfsParamsObj *paramsObj,
2984                                         int iNumberOfFeatures,
2985                                         int maxfeatures,
2986                                         WFSGMLInfo* pgmlinfo,
2987                                         rectObj bbox,
2988                                         const char* sBBoxSrs,
2989                                         char** layers,
2990                                         int numlayers,
2991                                         int nWFSVersion)
2992 {
2993   int nMatchingFeatures = -1;
2994 
2995   if( nWFSVersion >= OWS_2_0_0 )
2996   {
2997     /* If no features have been retrived and there was no feature count limit, then */
2998     /* it is obvious. We must test maxfeatures because if startindex is defined, */
2999     /* and above the number of matched features, iNumberOfFeatures can be 0. */
3000     if( iNumberOfFeatures == 0 && maxfeatures < 0 )
3001     {
3002         nMatchingFeatures = 0;
3003     }
3004 
3005     /* In the case we had limited the query by a maximum number of features, and */
3006     /* that the query has returned less features than the limit, or if there was */
3007     /* no limit in fature count, we are able to determine the number of matching */
3008     /* features */
3009     else if( iNumberOfFeatures > 0 && (maxfeatures < 0 || iNumberOfFeatures < maxfeatures) )
3010     {
3011         if( paramsObj->nStartIndex >= 0 )
3012             nMatchingFeatures = iNumberOfFeatures + paramsObj->nStartIndex;
3013         else
3014             nMatchingFeatures = iNumberOfFeatures;
3015     }
3016 
3017     /* Otherwise big hammer ! */
3018     else
3019     {
3020         /* The WFS 2.0 spec is not really clear on numberMatched behaviour */
3021         /* and there are interesting logic in server implementations. */
3022         /* */
3023         /* Deegree (http://deegree3-demo.deegree.org:80/inspire-workspace/services/wfs?) */
3024         /* will return numberMatched="19005" on the cp:CadastralParcel layer for a */
3025         /* resultType=hits request, whereas the server limitation is set to 15000 */
3026         /* But on a regular GetFeature (without count specified), it will return */
3027         /* numberMatched="15000" (and numberReturned="15000")) */
3028         /* */
3029         /* GeoServer 2.4.1 will also ignore its server limitation for a resultType=hits */
3030         /* but when using a regular GetFeature (without count specified), il will return */
3031         /* numberMatched=numberReturned=total_number_of_features_without_taking_into_account_server_limit */
3032         /* but the number of actually returned features will be truncated to the server */
3033         /* limit, likely a bug */
3034         /* */
3035         /* If wfs_compute_number_matched is set to TRUE, we will issue a full */
3036         /* count of potentially matching features, ignoring any client or server */
3037         /* side limits. */
3038 
3039         const char* pszComputeNumberMatched =
3040                 msOWSLookupMetadata(&(map->web.metadata), "F",
3041                                                 "compute_number_matched");
3042         if( pszComputeNumberMatched != NULL &&
3043             strcasecmp(pszComputeNumberMatched, "true") == 0 )
3044         {
3045             int j;
3046             mapObj* mapTmp = (mapObj*)msSmallCalloc(1, sizeof(mapObj));
3047             initMap(mapTmp);
3048             msCopyMap(mapTmp, map);
3049 
3050             /* Re-run the query but with no limit */
3051             mapTmp->query.maxfeatures = -1;
3052             mapTmp->query.startindex = -1;
3053             mapTmp->query.only_cache_result_count = MS_TRUE;
3054             for(j=0; j<mapTmp->numlayers; j++) {
3055                 layerObj* lp = GET_LAYER(mapTmp, j);
3056                 /* Reset layer paging */
3057                 lp->maxfeatures = -1;
3058                 lp->startindex = -1;
3059             }
3060 
3061             nMatchingFeatures = 0;
3062             msWFSRetrieveFeatures(mapTmp,
3063                                   ows_request,
3064                                 paramsObj,
3065                                 pgmlinfo,
3066                                 paramsObj->pszFilter,
3067                                 paramsObj->pszBbox != NULL,
3068                                 sBBoxSrs,
3069                                 bbox,
3070                                 paramsObj->pszFeatureId,
3071                                 layers,
3072                                 numlayers,
3073                                 -1,
3074                                 nWFSVersion,
3075                                 &nMatchingFeatures,
3076                                 NULL);
3077 
3078             msFreeMap(mapTmp);
3079         }
3080     }
3081   }
3082 
3083   return nMatchingFeatures;
3084 }
3085 
msWFSResolveStoredQuery(mapObj * map,wfsParamsObj * paramsObj,cgiRequestObj * req)3086 static int msWFSResolveStoredQuery(mapObj *map, wfsParamsObj *paramsObj, cgiRequestObj *req)
3087 {
3088   if (paramsObj->pszStoredQueryId != NULL )
3089   {
3090       int i;
3091       int status;
3092       hashTableObj* hashTable = msCreateHashTable();
3093       char* pszResolvedQuery;
3094 
3095       if (req->NumParams > 0 && req->postrequest == NULL ) {
3096         for(i=0; i<req->NumParams; i++) {
3097             if (req->ParamNames[i] && req->ParamValues[i]) {
3098                 msInsertHashTable(hashTable, req->ParamNames[i], req->ParamValues[i]);
3099             }
3100         }
3101       }
3102 
3103       pszResolvedQuery = msWFSGetResolvedStoredQuery20(map, paramsObj,
3104                                                        paramsObj->pszStoredQueryId,
3105                                                        hashTable);
3106       msFreeHashTable(hashTable);
3107 
3108       if( pszResolvedQuery == NULL )
3109           return MS_FAILURE;
3110 
3111       status = msWFSAnalyzeStoredQuery(map, paramsObj,
3112                                        paramsObj->pszStoredQueryId,
3113                                        pszResolvedQuery);
3114       msFree(pszResolvedQuery);
3115 
3116       if( status != MS_SUCCESS )
3117           return status;
3118 
3119       msWFSSimplifyPropertyNameAndFilter(paramsObj);
3120   }
3121   return MS_SUCCESS;
3122 }
3123 
3124 /*
3125 ** msWFSUnaliasItem()
3126 */
3127 static
msWFSUnaliasItem(layerObj * lp,const char * name)3128 const char* msWFSUnaliasItem(layerObj* lp, const char* name)
3129 {
3130     const char* pszFullName;
3131     char szTmp[256];
3132     int z;
3133     for(z=0; z<lp->numitems; z++) {
3134         if (!lp->items[z] || strlen(lp->items[z]) <= 0)
3135             continue;
3136         snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[z]);
3137         pszFullName = msOWSLookupMetadata(&(lp->metadata), "G", szTmp);
3138         if (pszFullName && strcmp(name, pszFullName) == 0)
3139             return lp->items[z];
3140     }
3141     return name;
3142 }
3143 
3144 /*
3145 ** msWFSIsAllowedItem()
3146 */
msWFSIsAllowedItem(gmlItemListObj * itemList,const char * name)3147 static int msWFSIsAllowedItem(gmlItemListObj *itemList , const char* name)
3148 {
3149     int z;
3150     for(z=0; z<itemList->numitems; z++) {
3151       if (itemList->items[z].visible &&
3152           strcasecmp(name, itemList->items[z].name) == 0) {
3153         return MS_TRUE;
3154       }
3155     }
3156     return MS_FALSE;
3157 }
3158 
3159 /*
3160 ** msWFSUngroupItem()
3161 */
3162 static
msWFSUngroupItem(layerObj * lp,gmlGroupListObj * groupList,const char * name,int * pnGroupIndex)3163 const char* msWFSUngroupItem(layerObj* lp, gmlGroupListObj* groupList,
3164                              const char* name, int* pnGroupIndex)
3165 {
3166     int z;
3167 
3168     *pnGroupIndex = -1;
3169     for(z=0; z<groupList->numgroups; z++) {
3170         const char* pszGroupName = groupList->groups[z].name;
3171         size_t nGroupNameLen = strlen(pszGroupName);
3172 
3173         /* Is it a group name ? */
3174         if(strcasecmp(name, pszGroupName) == 0)
3175         {
3176             *pnGroupIndex = z;
3177             return NULL;
3178         }
3179 
3180         /* Is it an item of a group ? */
3181         if(strncasecmp(name, pszGroupName, nGroupNameLen) == 0 &&
3182                        name[nGroupNameLen] == '/') {
3183 
3184             int w;
3185             const char* pszSubName = msWFSStripNS(name+nGroupNameLen+1);
3186             pszSubName = msWFSUnaliasItem(lp, pszSubName);
3187             for(w=0;w<groupList->groups[z].numitems;w++)
3188             {
3189                 if( strcasecmp(pszSubName, groupList->groups[z].items[w]) == 0 )
3190                 {
3191                     return pszSubName;
3192                 }
3193             }
3194         }
3195     }
3196 
3197     return name;
3198 }
3199 
3200 /*
3201 ** msWFSApplySortBy()
3202 */
msWFSApplySortBy(mapObj * map,wfsParamsObj * paramsObj,layerObj * lp,const char * pszSortBy,gmlItemListObj * itemList,gmlGroupListObj * groupList)3203 static int msWFSApplySortBy(mapObj* map, wfsParamsObj *paramsObj, layerObj* lp,
3204                             const char* pszSortBy,
3205                             gmlItemListObj *itemList,
3206                             gmlGroupListObj* groupList)
3207 {
3208     int y, n;
3209     char** tokens;
3210     int invalidSortBy = MS_FALSE;
3211     sortByClause sortBy;
3212 
3213     if( !msLayerSupportsSorting(lp) )
3214     {
3215         msSetError(MS_WFSERR, "Layer %s does not support sorting", "msWFSGetFeature()", lp->name);
3216         return msWFSException(map, "mapserv", MS_WFS_ERROR_OPERATION_PROCESSING_FAILED, paramsObj->pszVersion);
3217     }
3218 
3219     tokens = msStringSplit(pszSortBy, ',', &n);
3220     sortBy.nProperties = n;
3221     sortBy.properties = (sortByProperties* )msSmallMalloc(n * sizeof(sortByProperties));
3222     for(y=0; y<n; y++) {
3223         char* pszSpace;
3224         int nGroupIndex = -1;
3225         const char* pszSortProperty;
3226         char* pszTmp;
3227 
3228         sortBy.properties[y].item = msStrdup(tokens[y]);
3229         sortBy.properties[y].sortOrder = SORT_ASC;
3230         pszSpace = strchr(sortBy.properties[y].item, ' ');
3231         if( pszSpace )
3232         {
3233             *pszSpace = '\0';
3234             if( strcasecmp(pszSpace+1, "A") == 0 ||
3235                 strcasecmp(pszSpace+1, "ASC") == 0 )
3236                 ;
3237             else if( strcasecmp(pszSpace+1, "D") == 0 ||
3238                     strcasecmp(pszSpace+1, "DESC") == 0 )
3239                 sortBy.properties[y].sortOrder = SORT_DESC;
3240             else
3241                 invalidSortBy = MS_TRUE;
3242         }
3243 
3244         pszSortProperty = msWFSStripNS(sortBy.properties[y].item);
3245         pszSortProperty = msWFSUnaliasItem(lp, pszSortProperty);
3246         pszSortProperty = msWFSUngroupItem(lp, groupList, pszSortProperty, &nGroupIndex);
3247         if( nGroupIndex >= 0 )
3248             invalidSortBy = MS_TRUE;
3249 
3250         if( !msWFSIsAllowedItem(itemList, pszSortProperty) )
3251             invalidSortBy = MS_TRUE;
3252 
3253         pszTmp = msStrdup(pszSortProperty);
3254         msFree(sortBy.properties[y].item);
3255         sortBy.properties[y].item = pszTmp;
3256     }
3257     msFreeCharArray(tokens, n);
3258 
3259     if( !invalidSortBy )
3260         msLayerSetSort(lp, &sortBy);
3261 
3262     for(y=0; y<n; y++) {
3263         msFree(sortBy.properties[y].item);
3264     }
3265     msFree(sortBy.properties);
3266 
3267     if( invalidSortBy )
3268     {
3269         msSetError(MS_WFSERR, "Invalid SORTBY clause", "msWFSGetFeature()");
3270         return msWFSException(map, "sortby", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
3271     }
3272 
3273     return MS_SUCCESS;
3274 }
3275 
msWFSSetShapeCache(mapObj * map)3276 static void msWFSSetShapeCache(mapObj* map)
3277 {
3278     const char* pszFeaturesCacheCount =
3279                 msOWSLookupMetadata(&(map->web.metadata), "F",
3280                                                 "features_cache_count");
3281     const char* pszFeaturesCacheSize =
3282                 msOWSLookupMetadata(&(map->web.metadata), "F",
3283                                                 "features_cache_size");
3284     if( pszFeaturesCacheCount )
3285     {
3286         map->query.cache_shapes = MS_TRUE;
3287         map->query.max_cached_shape_count = atoi(pszFeaturesCacheCount);
3288         msDebug("Caching up to %d shapes\n", map->query.max_cached_shape_count);
3289     }
3290 
3291     if( pszFeaturesCacheSize )
3292     {
3293         map->query.cache_shapes = MS_TRUE;
3294         map->query.max_cached_shape_ram_amount = atoi(pszFeaturesCacheSize);
3295         if( strstr(pszFeaturesCacheSize, "mb") || strstr(pszFeaturesCacheSize, "MB") )
3296              map->query.max_cached_shape_ram_amount *= 1024 * 1024;
3297         msDebug("Caching up to %d bytes of shapes\n", map->query.max_cached_shape_ram_amount);
3298     }
3299 }
3300 
3301 /*
3302 ** msWFSGetFeature()
3303 */
3304 static
msWFSGetFeature(mapObj * map,wfsParamsObj * paramsObj,cgiRequestObj * req,owsRequestObj * ows_request,int nWFSVersion)3305 int msWFSGetFeature(mapObj *map, wfsParamsObj *paramsObj, cgiRequestObj *req,
3306                     owsRequestObj *ows_request, int nWFSVersion)
3307 {
3308   int   status;
3309   int   maxfeatures=-1,startindex=-1;
3310   rectObj     bbox;
3311 
3312   char **layers = NULL;
3313   int numlayers = 0;
3314 
3315   char *sBBoxSrs = NULL;
3316 
3317   WFSGMLInfo gmlinfo;
3318 
3319   OWSGMLVersion outputformat = OWS_GML2; /* default output is GML 2.1 */
3320   outputFormatObj *psFormat = NULL;
3321 
3322   int iNumberOfFeatures = 0;
3323   int iResultTypeHits = 0;
3324   int nMatchingFeatures = -1;
3325   int bHasNextFeatures = MS_FALSE;
3326 
3327   char** papszGMLGroups = NULL;
3328   char** papszGMLIncludeItems = NULL;
3329   char** papszGMLGeometries = NULL;
3330 
3331   msIOContext* old_context = NULL;
3332 
3333   /* Initialize gml options */
3334   msWFSInitGMLInfo(&gmlinfo);
3335 
3336   if (paramsObj->pszResultType != NULL) {
3337     if (strcasecmp(paramsObj->pszResultType, "hits") == 0)
3338       iResultTypeHits = 1;
3339   }
3340 
3341   status = msWFSResolveStoredQuery(map, paramsObj, req);
3342   if( status != MS_SUCCESS )
3343       return status;
3344 
3345   /* typename is mandatory unless featureid is specfied. We do not
3346      support featureid */
3347   if (paramsObj->pszTypeName==NULL && paramsObj->pszFeatureId == NULL) {
3348     msSetError(MS_WFSERR,
3349                "Incomplete WFS request: %s parameter missing",
3350                "msWFSGetFeature()",
3351                (nWFSVersion >= OWS_2_0_0 ) ? "TYPENAMES": "TYPENAME" );
3352     return msWFSException(map, (nWFSVersion >= OWS_2_0_0 ) ? "typenames": "typename",
3353                           MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
3354   }
3355 
3356   /* Is it a WFS 2.0 GetFeatureById stored query with an unknown id ? */
3357   /* If so, CITE interprets the spec as requesting to issue a OperationProcessingFailed */
3358   /* exception. A bit strange when a similar request but with RESOURCEID= should */
3359   /* produce an empty FeatureCollection */
3360   if( nWFSVersion >= OWS_2_0_0 &&
3361       paramsObj->pszTypeName != NULL && strcmp(paramsObj->pszTypeName, "?") == 0 &&
3362       paramsObj->pszFilter != NULL && paramsObj->pszFeatureId == NULL )
3363   {
3364     CPLXMLNode* psDoc;
3365 
3366     psDoc = CPLParseXMLString(paramsObj->pszFilter);
3367     if( psDoc != NULL )
3368     {
3369         const char* rid;
3370 
3371         CPLStripXMLNamespace(psDoc, NULL, 1);
3372         rid = CPLGetXMLValue(psDoc, "=Filter.ResourceId.rid", NULL);
3373         if( rid != NULL )
3374         {
3375             msSetError(MS_WFSERR, "Invalid rid '%s'", "msWFSGetFeature()", rid);
3376             CPLDestroyXMLNode(psDoc);
3377             return msWFSException(map, NULL, MS_WFS_ERROR_OPERATION_PROCESSING_FAILED,
3378                                   paramsObj->pszVersion );
3379         }
3380         CPLDestroyXMLNode(psDoc);
3381     }
3382   }
3383 
3384   papszGMLGroups = (char**) calloc(sizeof(char*), map->numlayers);
3385   papszGMLIncludeItems = (char**) calloc(sizeof(char*), map->numlayers);
3386   papszGMLGeometries = (char**) calloc(sizeof(char*), map->numlayers);
3387 
3388   if(paramsObj->pszTypeName) {
3389     int k, y,z;
3390 
3391     char **tokens;
3392     int n=0, i=0;
3393     char **papszPropertyName = NULL;
3394     char **papszSortBy = NULL;
3395 
3396     /* keep a ref for layer use. */
3397     gmlinfo.typename = paramsObj->pszTypeName;
3398 
3399     /* Parse comma-delimited list of type names (layers) */
3400     /*  */
3401     /* __TODO__ Need to handle type grouping, e.g. "(l1,l2),l3,l4" */
3402     /*  */
3403     layers = msStringSplit(gmlinfo.typename, ',', &numlayers);
3404 
3405     /* ==================================================================== */
3406     /*      TODO: check if the typename contains namespaces (ex cdf:Other), */
3407     /*      If that is the case extract only the layer name.                */
3408     /* ==================================================================== */
3409 
3410     if (layers==NULL || numlayers < 1) {
3411       msSetError(MS_WFSERR, "At least one type name required in %s parameter.", "msWFSGetFeature()",
3412                  (nWFSVersion >= OWS_2_0_0 ) ? "TYPENAMES": "TYPENAME" );
3413       return msWFSException(map, (nWFSVersion >= OWS_2_0_0 ) ? "typenames": "typename" ,
3414                             MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
3415     }
3416 
3417     /* strip namespace if there is one :ex TYPENAME=cdf:Other */
3418     for (i=0; i<numlayers; i++) {
3419       char* pszTmp = msStrdup(msWFSStripNS(layers[i]));
3420       free(layers[i]);
3421       layers[i] = pszTmp;
3422     }
3423 
3424     /* Keep only selected layers, set to OFF by default. */
3425     msWFSTurnOffAllLayer(map);
3426 
3427     papszPropertyName = msWFSParsePropertyNameOrSortBy(paramsObj->pszPropertyName, numlayers);
3428     if( papszPropertyName == NULL ) {
3429         msFreeCharArray(layers, numlayers);
3430         msFreeCharArray(papszGMLGroups, map->numlayers);
3431         msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3432         msFreeCharArray(papszGMLGeometries, map->numlayers);
3433         msSetError(MS_WFSERR,
3434                     "Optional PROPERTYNAME parameter. A list of properties may be specified for each type name. Example TYPENAME=name1,name2&PROPERTYNAME=(prop1,prop2)(prop1)",
3435                     "msWFSGetFeature()");
3436         return msWFSException(map, "PROPERTYNAME", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
3437     }
3438 
3439     papszSortBy = msWFSParsePropertyNameOrSortBy(paramsObj->pszSortBy, numlayers);
3440     if( papszSortBy == NULL ) {
3441         msFreeCharArray(layers, numlayers);
3442         msFreeCharArray(papszPropertyName, numlayers);
3443         msFreeCharArray(papszGMLGroups, map->numlayers);
3444         msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3445         msFreeCharArray(papszGMLGeometries, map->numlayers);
3446         msSetError(MS_WFSERR,
3447                     "Optional SORTBY parameter. A list may be specified for each type name. Example TYPENAME=name1,name2&SORTBY=(prop1,prop2)(prop1)",
3448                     "msWFSGetFeature()");
3449         return msWFSException(map, "SORTBY", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
3450     }
3451 
3452     for (k=0; k<numlayers; k++) {
3453       layerObj *lp;
3454       lp = msWFSGetLayerByName(map, ows_request, layers[k]);
3455       if( lp != NULL )
3456       {
3457             gmlItemListObj *itemList=NULL;
3458             gmlGeometryListObj *geometryList=NULL;
3459             gmlGroupListObj* groupList=NULL;
3460 
3461             lp->status = MS_ON;
3462             if (lp->template == NULL) {
3463               /* Force setting a template to enable query. */
3464               lp->template = msStrdup("ttt.html");
3465             }
3466 
3467             /*do an alias replace for the current layer*/
3468             if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
3469 
3470               itemList = msGMLGetItems(lp, "G");
3471               groupList = msGMLGetGroups(lp, "G");
3472 
3473               if( strcmp(papszSortBy[k], "!") != 0 )
3474               {
3475                   status = msWFSApplySortBy(map, paramsObj, lp,
3476                                             papszSortBy[k], itemList, groupList);
3477                   if( status != MS_SUCCESS )
3478                   {
3479                     msFreeCharArray(papszPropertyName, numlayers);
3480                     msFreeCharArray(papszSortBy, numlayers);
3481                     msFreeCharArray(layers, numlayers);
3482                     msFreeCharArray(papszGMLGroups, map->numlayers);
3483                     msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3484                     msFreeCharArray(papszGMLGeometries, map->numlayers);
3485                     msGMLFreeItems(itemList);
3486                     msGMLFreeGroups(groupList);
3487                     return status;
3488                   }
3489               }
3490 
3491               geometryList=msGMLGetGeometries(lp, "GFO", MS_TRUE);
3492 
3493               /* unalias, ungroup and validate that the property names passed are part of the items list*/
3494               tokens = msStringSplit(papszPropertyName[k], ',', &n);
3495               for (y=0; y<n; y++) {
3496                 const char* pszPropertyNameItem = tokens[y];
3497                 int nGroupIndex = -1;
3498 
3499                 if (strcasecmp(pszPropertyNameItem, "*") == 0 ||
3500                     strcasecmp(pszPropertyNameItem, "!") == 0)
3501                   continue;
3502 
3503                 pszPropertyNameItem = msWFSStripNS(pszPropertyNameItem);
3504                 pszPropertyNameItem = msWFSUnaliasItem(lp, pszPropertyNameItem);
3505                 pszPropertyNameItem = msWFSUngroupItem(lp, groupList, pszPropertyNameItem, &nGroupIndex);
3506                 if( nGroupIndex >= 0 )
3507                   continue;
3508 
3509                 /* Check that the property name is allowed (#3563) */
3510                 /*we need to check of the property name is the geometry name; In that case it
3511                   is a valid property name*/
3512                 if (!msWFSIsAllowedItem(itemList,pszPropertyNameItem)) {
3513                   for(z=0; z<geometryList->numgeometries; z++) {
3514                     if (strcasecmp(pszPropertyNameItem, geometryList->geometries[z].name) == 0)
3515                       break;
3516                   }
3517 
3518                   if (z == geometryList->numgeometries) {
3519                     msSetError(MS_WFSERR,
3520                                "Invalid PROPERTYNAME %s",  "msWFSGetFeature()", tokens[y]);
3521                     msFreeCharArray(tokens, n);
3522                     msGMLFreeItems(itemList);
3523                     msGMLFreeGroups(groupList);
3524                     msGMLFreeGeometries(geometryList);
3525                     msFreeCharArray(papszPropertyName, numlayers);
3526                     msFreeCharArray(papszSortBy, numlayers);
3527                     msFreeCharArray(layers, numlayers);
3528                     msFreeCharArray(papszGMLGroups, map->numlayers);
3529                     msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3530                     msFreeCharArray(papszGMLGeometries, map->numlayers);
3531                     return msWFSException(map, "PROPERTYNAME", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
3532                   }
3533                 }
3534                 else
3535                 {
3536                   char* pszTmp = msStrdup(pszPropertyNameItem);
3537                   msFree(tokens[y]);
3538                   tokens[y] = pszTmp;
3539                 }
3540               }
3541 
3542               /* Add mandatory items that are not explicitely listed. */
3543               /* When the user specifies PROPERTYNAME, we might have to augment */
3544               /* the list of returned parameters so as to validate against the schema. */
3545               /* This is made clear in the WFS 1.0.0, 1.1.0 and 2.0.0 specs (#3319) */
3546               for(z=0; z<itemList->numitems; z++) {
3547                 if( itemList->items[z].visible &&
3548                     itemList->items[z].minOccurs == 1 )
3549                 {
3550                     int bFound = MS_FALSE;
3551                     for (y=0; y<n; y++) {
3552                     if( strcasecmp(tokens[y], "*") == 0 ||
3553                         strcasecmp(tokens[y], "!") == 0 ||
3554                         strcasecmp(tokens[y], itemList->items[z].name) == 0 )
3555                     {
3556                         bFound = MS_TRUE;
3557                         break;
3558                     }
3559                     }
3560                     if( !bFound )
3561                     {
3562                         tokens = (char**) realloc(tokens, sizeof(char*) * (n+1));
3563                         tokens[n] = msStrdup(itemList->items[z].name);
3564                         n ++;
3565                     }
3566                 }
3567               }
3568 
3569               /* Rebuild papszPropertyName[k] */
3570               msFree(papszPropertyName[k]);
3571               papszPropertyName[k] = NULL;
3572               for (y=0; y<n; y++) {
3573                 if( y == 0 )
3574                   papszPropertyName[k] = msStrdup(tokens[y]);
3575                 else {
3576                   papszPropertyName[k] = msStringConcatenate(
3577                             papszPropertyName[k], ",");
3578                   papszPropertyName[k] = msStringConcatenate(
3579                             papszPropertyName[k], tokens[y]);
3580                 }
3581               }
3582 
3583 
3584               msFreeCharArray(tokens, n);
3585               msLayerClose(lp);
3586 
3587               if (strlen(papszPropertyName[k]) > 0) {
3588 
3589                 if (strcasecmp(papszPropertyName[k], "*") == 0) {
3590                   /* Add all non-excluded items, including optional ones */
3591                   msFree(papszPropertyName[k]);
3592                   papszPropertyName[k] = NULL;
3593                   for(z=0; z<itemList->numitems; z++) {
3594                     if( itemList->items[z].visible ) {
3595                         if( papszPropertyName[k] != NULL )
3596                             papszPropertyName[k] = msStringConcatenate(
3597                                 papszPropertyName[k], ",");
3598                         papszPropertyName[k] = msStringConcatenate(
3599                             papszPropertyName[k], itemList->items[z].name);
3600                     }
3601                   }
3602                   if( papszPropertyName[k] )
3603                     papszGMLIncludeItems[lp->index] = msStrdup(papszPropertyName[k]);
3604                   else
3605                     papszGMLIncludeItems[lp->index] = msStrdup("");
3606 
3607                 /* The user didn't specify explicity PROPERTYNAME for this layer */
3608                 } else if (strcasecmp(papszPropertyName[k], "!") == 0) {
3609                   /* Add all non-excluded items, but only default and mandatory ones (and geometry) */
3610                   msFree(papszPropertyName[k]);
3611                   papszPropertyName[k] = NULL;
3612                   for(z=0; z<itemList->numitems; z++) {
3613                     if( itemList->items[z].visible &&
3614                         (itemList->items[z].outputByDefault == 1 ||
3615                          itemList->items[z].minOccurs == 1) ) {
3616                         if( papszPropertyName[k] != NULL )
3617                             papszPropertyName[k] = msStringConcatenate(
3618                                 papszPropertyName[k], ",");
3619                         papszPropertyName[k] = msStringConcatenate(
3620                             papszPropertyName[k], itemList->items[z].name);
3621                     }
3622                   }
3623                   if( papszPropertyName[k] )
3624                     papszGMLIncludeItems[lp->index] = msStrdup(papszPropertyName[k]);
3625                   else
3626                     papszGMLIncludeItems[lp->index] = msStrdup("");
3627 
3628                 } else {
3629                   char* pszGeometryList = NULL;
3630 
3631                   for(z=0; z<groupList->numgroups; z++) {
3632                     const char* pszNeedle = strstr(papszPropertyName[k], groupList->groups[z].name);
3633                     char chAfterNeedle = 0;
3634                     if( pszNeedle != NULL )
3635                         chAfterNeedle = papszPropertyName[k][strlen(groupList->groups[z].name)];
3636                     if(pszNeedle != NULL && (chAfterNeedle == '\0' || chAfterNeedle == ',') ) {
3637                       int w;
3638                       for(w=0;w<groupList->groups[z].numitems;w++)
3639                       {
3640                         if(strstr(papszPropertyName[k], groupList->groups[z].items[w]) == NULL) {
3641                             papszPropertyName[k] = msStringConcatenate(
3642                                 papszPropertyName[k], ",");
3643                             papszPropertyName[k] = msStringConcatenate(
3644                                 papszPropertyName[k], groupList->groups[z].items[w]);
3645                         }
3646                       }
3647                     }
3648                   }
3649 
3650                   papszGMLIncludeItems[lp->index] = msStrdup(papszPropertyName[k]);
3651 
3652                   for(z=0; z<geometryList->numgeometries; z++) {
3653                     if (strstr(papszPropertyName[k], geometryList->geometries[z].name) != NULL) {
3654                         if( pszGeometryList != NULL )
3655                             pszGeometryList = msStringConcatenate(
3656                                 pszGeometryList, ",");
3657                         pszGeometryList = msStringConcatenate(
3658                             pszGeometryList, geometryList->geometries[z].name);
3659                     }
3660                   }
3661 
3662                   if( pszGeometryList != NULL ) {
3663                       papszGMLGeometries[lp->index] = msStrdup(pszGeometryList);
3664                       msFree(pszGeometryList);
3665                   } else {
3666                     papszGMLGeometries[lp->index] = msStrdup("none");
3667                   }
3668                 }
3669               } else {/*empty string*/
3670                 papszGMLGeometries[lp->index] = msStrdup("none");
3671               }
3672             }
3673             else
3674             {
3675                 msFreeCharArray(papszPropertyName, numlayers);
3676                 msFreeCharArray(papszSortBy, numlayers);
3677                 msFreeCharArray(layers, numlayers);
3678                 msFreeCharArray(papszGMLGroups, map->numlayers);
3679                 msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3680                 msFreeCharArray(papszGMLGeometries, map->numlayers);
3681                 msGMLFreeItems(itemList);
3682                 msGMLFreeGroups(groupList);
3683                 msGMLFreeGeometries(geometryList);
3684                 return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
3685             }
3686 
3687             msGMLFreeItems(itemList);
3688             msGMLFreeGroups(groupList);
3689             msGMLFreeGeometries(geometryList);
3690       }
3691       else
3692       {
3693         /* Requested layer name was not in capabilities:
3694          * either it just doesn't exist
3695          */
3696         msSetError(MS_WFSERR,
3697                    "TYPENAME '%s' doesn't exist in this server.  Please check the capabilities and reformulate your request.",
3698                    "msWFSGetFeature()", layers[k]);
3699         msFreeCharArray(papszPropertyName, numlayers);
3700         msFreeCharArray(papszSortBy, numlayers);
3701         msFreeCharArray(layers, numlayers);
3702         msFreeCharArray(papszGMLGroups, map->numlayers);
3703         msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3704         msFreeCharArray(papszGMLGeometries, map->numlayers);
3705         return msWFSException(map, "typename", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
3706       }
3707     }
3708 
3709     msFreeCharArray(papszPropertyName, numlayers);
3710     msFreeCharArray(papszSortBy, numlayers);
3711   }
3712 
3713   /* Validate outputformat */
3714   status = msWFSGetGMLOutputFormat(map, paramsObj, &gmlinfo, nWFSVersion);
3715   if( status < 0 ) {
3716     psFormat = msWFSGetOtherOutputFormat(map, paramsObj);
3717     if( psFormat == NULL ) {
3718       msFreeCharArray(layers, numlayers);
3719       msFreeCharArray(papszGMLGroups, map->numlayers);
3720       msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3721       msFreeCharArray(papszGMLGeometries, map->numlayers);
3722       return msWFSException(map, "outputformat",
3723                             MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3724                             paramsObj->pszVersion );
3725     }
3726   }
3727   else {
3728     outputformat = (OWSGMLVersion) status;
3729   }
3730 
3731   msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, iResultTypeHits,
3732                                         &maxfeatures, &startindex);
3733 
3734   status = msWFSAnalyzeBBOX(map, paramsObj, &bbox, &sBBoxSrs);
3735   if( status != 0 )
3736   {
3737       msFreeCharArray(layers, numlayers);
3738       msFreeCharArray(papszGMLGroups, map->numlayers);
3739       msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3740       msFreeCharArray(papszGMLGeometries, map->numlayers);
3741       return status;
3742   }
3743 
3744   if( iResultTypeHits == 1 )
3745   {
3746       map->query.only_cache_result_count = MS_TRUE;
3747   }
3748   else
3749   {
3750       msWFSSetShapeCache(map);
3751   }
3752 
3753   status = msWFSRetrieveFeatures(map,
3754                                  ows_request,
3755                            paramsObj,
3756                            &gmlinfo,
3757                            paramsObj->pszFilter,
3758                            paramsObj->pszBbox != NULL,
3759                            sBBoxSrs,
3760                            bbox,
3761                            paramsObj->pszFeatureId,
3762                            layers,
3763                            numlayers,
3764                            maxfeatures,
3765                            nWFSVersion,
3766                            &iNumberOfFeatures,
3767                            &bHasNextFeatures);
3768   if( status != MS_SUCCESS )
3769   {
3770       msFreeCharArray(layers, numlayers);
3771       msFree(sBBoxSrs);
3772       msFreeCharArray(papszGMLGroups, map->numlayers);
3773       msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3774       msFreeCharArray(papszGMLGeometries, map->numlayers);
3775       return status;
3776   }
3777 
3778   /* ----------------------------------------- */
3779   /* Now compute nMatchingFeatures for WFS 2.0 */
3780   /* ----------------------------------------- */
3781 
3782   nMatchingFeatures = msWFSComputeMatchingFeatures(map,ows_request,paramsObj,
3783                                                    iNumberOfFeatures,
3784                                                    maxfeatures,
3785                                                    &gmlinfo,
3786                                                    bbox,
3787                                                    sBBoxSrs,
3788                                                    layers,
3789                                                    numlayers,
3790                                                    nWFSVersion);
3791 
3792   msFreeCharArray(layers, numlayers);
3793 
3794   msFree(sBBoxSrs);
3795   sBBoxSrs = NULL;
3796 
3797   /*
3798   ** GML Header generation.
3799   */
3800 
3801   status = MS_SUCCESS;
3802 
3803   if( psFormat == NULL ) {
3804     if( paramsObj->countGetFeatureById == 1 && maxfeatures != 0 && iResultTypeHits == 0 )
3805     {
3806         /* When there's a single  urn:ogc:def:query:OGC-WFS::GetFeatureById GetFeature request */
3807         /* we must not output a FeatureCollection but the object itself */
3808         /* We do that as a post-processing step, so let print into a buffer and modify the */
3809         /* XML afterwards */
3810         old_context = msIO_pushStdoutToBufferAndGetOldContext();
3811     }
3812     else
3813     {
3814         msIO_setHeader("Content-Type","%s; charset=UTF-8", gmlinfo.output_mime_type);
3815         msIO_sendHeaders();
3816     }
3817 
3818     status = msWFSGetFeature_GMLPreamble( map, req, &gmlinfo, paramsObj,
3819                                  outputformat,
3820                                  iResultTypeHits,
3821                                  iNumberOfFeatures,
3822                                  nMatchingFeatures,
3823                                  maxfeatures,
3824                                  bHasNextFeatures,
3825                                  nWFSVersion );
3826     if(status != MS_SUCCESS) {
3827       if( old_context != NULL )
3828           msIO_restoreOldStdoutContext(old_context);
3829       msWFSCleanupGMLInfo(&gmlinfo);
3830       msFreeCharArray(papszGMLGroups, map->numlayers);
3831       msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3832       msFreeCharArray(papszGMLGeometries, map->numlayers);
3833       return MS_FAILURE;
3834     }
3835   }
3836 
3837   {
3838     int i;
3839     for(i=0;i<map->numlayers;i++)
3840     {
3841       layerObj* lp = GET_LAYER(map, i);
3842       if( papszGMLGroups[i] )
3843           msInsertHashTable(&(lp->metadata), "GML_GROUPS", papszGMLGroups[i]);
3844       if( papszGMLIncludeItems[i] )
3845           msInsertHashTable(&(lp->metadata), "GML_INCLUDE_ITEMS", papszGMLIncludeItems[i]);
3846       if( papszGMLGeometries[i] )
3847           msInsertHashTable(&(lp->metadata), "GML_GEOMETRIES", papszGMLGeometries[i]);
3848     }
3849   }
3850 
3851   /* handle case of maxfeatures = 0 */
3852   /*internally use a start index that start with 0 as the first index*/
3853   if( psFormat == NULL ) {
3854     if(maxfeatures != 0 && iResultTypeHits == 0)
3855     {
3856       int bWFS2MultipleFeatureCollection = MS_FALSE;
3857 
3858       /* Would make sense for WFS 1.1.0 too ! See #3576 */
3859       int bUseURN = (nWFSVersion == OWS_2_0_0);
3860       const char* useurn = msOWSLookupMetadata(&(map->web.metadata), "F", "return_srs_as_urn");
3861       if (useurn && strcasecmp(useurn, "true") == 0)
3862         bUseURN = 1;
3863       else if (useurn && strcasecmp(useurn, "false") == 0)
3864         bUseURN = 0;
3865 
3866       /* For WFS 2.0, when we request several types, we must present each type */
3867       /* in its own FeatureCollection (§ 11.3.3.5 ) */
3868       if( nWFSVersion >= OWS_2_0_0 && iResultTypeHits != 1 )
3869       {
3870           int i;
3871           int nLayersWithFeatures = 0;
3872           for(i=0; i<map->numlayers; i++) {
3873               layerObj* lp = GET_LAYER(map, i);
3874               if(lp->resultcache && lp->resultcache->numresults > 0)
3875                   nLayersWithFeatures ++;
3876           }
3877           if( nLayersWithFeatures > 1 )
3878           {
3879             char timestring[100];
3880             resultCacheObj** saveResultCache = (resultCacheObj** )
3881                     msSmallMalloc( map->numlayers * sizeof(resultCacheObj*));
3882             int iLastNonEmptyLayer = -1;
3883 
3884             msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
3885 
3886             /* Emit global bounds */
3887             msGMLWriteWFSBounds(map, stdout, "  ", outputformat, nWFSVersion, bUseURN);
3888 
3889             /* Save the result cache that contains the features that we want to */
3890             /* emit in the response */
3891             for(i=0; i<map->numlayers; i++) {
3892               layerObj* lp = GET_LAYER(map, i);
3893               saveResultCache[i] = lp->resultcache;
3894               if( lp->resultcache  && lp->resultcache->numresults > 0) {
3895                   iLastNonEmptyLayer = i;
3896               }
3897               lp->resultcache = NULL;
3898             }
3899 
3900             /* Just dump one layer at a time */
3901             for(i=0;i<map->numlayers;i++) {
3902               layerObj* lp = GET_LAYER(map, i);
3903               lp->resultcache = saveResultCache[i];
3904               if( lp->resultcache  && lp->resultcache->numresults > 0) {
3905                 msIO_fprintf(stdout, "  <wfs:member>\n");
3906                 msIO_fprintf(stdout, "   <wfs:FeatureCollection timeStamp=\"%s\" numberMatched=\"", timestring);
3907                 if( i < iLastNonEmptyLayer || nMatchingFeatures >= 0 )
3908                     msIO_fprintf(stdout, "%d", lp->resultcache->numresults );
3909                 else
3910                     msIO_fprintf(stdout, "unknown" );
3911                 msIO_fprintf(stdout, "\" numberReturned=\"%d\">\n", lp->resultcache->numresults );
3912 
3913                 msGMLWriteWFSQuery(map, stdout,
3914                                     gmlinfo.user_namespace_prefix,
3915                                     outputformat,
3916                                     nWFSVersion,
3917                                     bUseURN,
3918                                     MS_FALSE);
3919                 msIO_fprintf(stdout, "   </wfs:FeatureCollection>\n");
3920                 msIO_fprintf(stdout, "  </wfs:member>\n");
3921               }
3922               lp->resultcache = NULL;
3923             }
3924 
3925             /* Restore for later cleanup */
3926             for(i=0; i<map->numlayers; i++) {
3927               layerObj* lp = GET_LAYER(map, i);
3928               lp->resultcache = saveResultCache[i];
3929             }
3930             msFree(saveResultCache);
3931 
3932             bWFS2MultipleFeatureCollection = MS_TRUE;
3933          }
3934       }
3935 
3936       if( !bWFS2MultipleFeatureCollection )
3937       {
3938         msGMLWriteWFSQuery(map, stdout,
3939                                     gmlinfo.user_namespace_prefix,
3940                                     outputformat,
3941                                     nWFSVersion,
3942                                     bUseURN,
3943                                     MS_FALSE);
3944       }
3945 
3946       status =  MS_SUCCESS;
3947     }
3948   } else {
3949     mapservObj *mapserv = msAllocMapServObj();
3950 
3951     /* Setup dummy mapserv object */
3952     mapserv->sendheaders = MS_TRUE;
3953     mapserv->map = map;
3954     msFreeCgiObj(mapserv->request);
3955     mapserv->request = req;
3956     map->querymap.status = MS_FALSE;
3957 
3958     if( nMatchingFeatures >= 0 )
3959     {
3960         char szMatchingFeatures[12];
3961         sprintf(szMatchingFeatures, "%d", nMatchingFeatures);
3962         msSetOutputFormatOption( psFormat, "_matching_features_",
3963                                  szMatchingFeatures);
3964     }
3965     else
3966     {
3967         msSetOutputFormatOption( psFormat, "_matching_features_", "");
3968     }
3969     status = msReturnTemplateQuery( mapserv, psFormat->name, NULL );
3970 
3971     mapserv->request = NULL;
3972     mapserv->map = NULL;
3973 
3974     msFreeMapServObj( mapserv );
3975 
3976     if( status != MS_SUCCESS ) {
3977       msFreeCharArray(papszGMLGroups, map->numlayers);
3978       msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3979       msFreeCharArray(papszGMLGeometries, map->numlayers);
3980       return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
3981                             paramsObj->pszVersion );
3982     }
3983   }
3984 
3985   msFreeCharArray(papszGMLGroups, map->numlayers);
3986   msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3987   msFreeCharArray(papszGMLGeometries, map->numlayers);
3988 
3989   if( psFormat == NULL && status == MS_SUCCESS ) {
3990     msWFSGetFeature_GMLPostfix( map, req, &gmlinfo, paramsObj,
3991                                 outputformat,
3992                                 maxfeatures, iResultTypeHits, iNumberOfFeatures,
3993                                 nWFSVersion );
3994   }
3995 
3996   /*Special case for a single urn:ogc:def:query:OGC-WFS::GetFeatureById GetFeature request */
3997   if( old_context != NULL )
3998   {
3999     msIOContext* new_context;
4000     msIOBuffer* buffer;
4001     CPLXMLNode* psRoot;
4002 
4003     new_context = msIO_getHandler(stdout);
4004     buffer = (msIOBuffer *) new_context->cbData;
4005     psRoot = CPLParseXMLString((const char*) buffer->data);
4006     msIO_restoreOldStdoutContext(old_context);
4007     if( psRoot != NULL )
4008     {
4009         CPLXMLNode* psFeatureCollection = CPLGetXMLNode(psRoot, "=wfs:FeatureCollection");
4010         if( psFeatureCollection != NULL )
4011         {
4012             CPLXMLNode* psMember = CPLGetXMLNode(psFeatureCollection, "wfs:member");
4013             if( psMember != NULL && psMember->psChild != NULL )
4014             {
4015                 CPLXMLNode* psIter;
4016                 CPLXMLNode* psFeatureNode;
4017                 char* pszTmpXML;
4018 
4019                 /* Transfer all namespaces of FeatureCollection to the Feature */
4020                 psFeatureNode = psMember->psChild;
4021                 psIter = psFeatureCollection->psChild;
4022                 while(psIter != NULL)
4023                 {
4024                     if( psIter->eType == CXT_Attribute &&
4025                         (strncmp(psIter->pszValue, "xmlns:", strlen("xmlns:")) == 0 ||
4026                          strncmp(psIter->pszValue, "xsi:", strlen("xsi:")) == 0) )
4027                     {
4028                         CPLXMLNode* psIterNext = psIter->psNext;
4029                         psIter->psNext = NULL;
4030                         CPLAddXMLChild(psFeatureNode, CPLCloneXMLTree(psIter));
4031                         psIter->psNext = psIterNext;
4032                     }
4033                     psIter = psIter->psNext;
4034                 }
4035 
4036                 pszTmpXML = CPLSerializeXMLTree(psFeatureNode);
4037                 msIO_setHeader("Content-Type","%s; charset=UTF-8", gmlinfo.output_mime_type);
4038                 msIO_sendHeaders();
4039                 msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
4040                 msIO_printf("%s", pszTmpXML);
4041                 CPLFree(pszTmpXML);
4042             }
4043             else
4044             {
4045                 status = msWFSException(map, NULL, MS_WFS_ERROR_OPERATION_PROCESSING_FAILED,
4046                                         paramsObj->pszVersion );
4047             }
4048         }
4049         CPLDestroyXMLNode(psRoot);
4050     }
4051   }
4052 
4053   msWFSCleanupGMLInfo(&gmlinfo);
4054 
4055   return status;
4056 }
4057 
4058 /*
4059 ** msWFSGetPropertyValue()
4060 */
4061 static
msWFSGetPropertyValue(mapObj * map,wfsParamsObj * paramsObj,cgiRequestObj * req,owsRequestObj * ows_request,int nWFSVersion)4062 int msWFSGetPropertyValue(mapObj *map, wfsParamsObj *paramsObj, cgiRequestObj *req,
4063                          owsRequestObj *ows_request, int nWFSVersion)
4064 {
4065   int   status;
4066   int   maxfeatures=-1,startindex=-1;
4067   rectObj     bbox;
4068 
4069   char *sBBoxSrs = NULL;
4070 
4071   WFSGMLInfo gmlinfo;
4072 
4073   OWSGMLVersion outputformat = OWS_GML2; /* default output is GML 2.1 */
4074 
4075   int iNumberOfFeatures = 0;
4076   int iResultTypeHits = 0;
4077   int nMatchingFeatures = -1;
4078   int bHasNextFeatures = MS_FALSE;
4079 
4080   const char* pszTypeName;
4081   layerObj *lp;
4082 
4083   char* pszGMLGroups = NULL;
4084   char* pszGMLIncludeItems = NULL;
4085   char* pszGMLGeometries = NULL;
4086 
4087   status = msWFSResolveStoredQuery(map, paramsObj, req);
4088   if( status != MS_SUCCESS )
4089       return status;
4090 
4091   if (paramsObj->pszTypeName==NULL) {
4092     msSetError(MS_WFSERR,
4093                "Incomplete WFS request: TYPENAMES parameter missing",
4094                "msWFSGetPropertyValue()");
4095     return msWFSException(map, "typenames",
4096                           MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
4097   }
4098 
4099   if (paramsObj->pszValueReference==NULL) {
4100     msSetError(MS_WFSERR,
4101                "Incomplete WFS request: VALUEREFERENCE parameter missing",
4102                "msWFSGetPropertyValue()");
4103     return msWFSException(map, "valuereference",
4104                           MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
4105   }
4106 
4107   /* Keep only selected layers, set to OFF by default. */
4108   msWFSTurnOffAllLayer(map);
4109 
4110   /* Remove namespace prefix */
4111   pszTypeName = msWFSStripNS(paramsObj->pszTypeName);
4112 
4113   lp = msWFSGetLayerByName(map, ows_request, pszTypeName);
4114   if( lp != NULL ) {
4115         gmlItemListObj *itemList=NULL;
4116         gmlGeometryListObj *geometryList=NULL;
4117         gmlGroupListObj* groupList=NULL;
4118 
4119         lp->status = MS_ON;
4120         if (lp->template == NULL) {
4121             /* Force setting a template to enable query. */
4122             lp->template = msStrdup("ttt.html");
4123         }
4124 
4125         /*do an alias replace for the current layer*/
4126         if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
4127             const char* pszValueReference;
4128             int z;
4129             int nGroupIndex = -1;
4130 
4131             itemList = msGMLGetItems(lp, "G");
4132             groupList = msGMLGetGroups(lp, "G");
4133 
4134             if( paramsObj->pszSortBy != NULL &&
4135                 strcmp(paramsObj->pszSortBy, "!") != 0 )
4136             {
4137               status = msWFSApplySortBy(map, paramsObj, lp,
4138                                        paramsObj->pszSortBy, itemList, groupList);
4139               if( status != MS_SUCCESS )
4140               {
4141                 msGMLFreeItems(itemList);
4142                 msGMLFreeGroups(groupList);
4143                 return status;
4144               }
4145             }
4146 
4147             geometryList=msGMLGetGeometries(lp, "GFO", MS_TRUE);
4148 
4149             pszValueReference = msWFSStripNS(paramsObj->pszValueReference);
4150             pszValueReference = msWFSUnaliasItem(lp, pszValueReference);
4151             pszValueReference = msWFSUngroupItem(lp, groupList, pszValueReference, &nGroupIndex);
4152 
4153             if( strcmp(paramsObj->pszValueReference, "@gml:id") == 0 ) {
4154               pszGMLGroups = msStrdup("");
4155               pszGMLIncludeItems = msStrdup(paramsObj->pszValueReference);
4156               pszGMLGeometries = msStrdup("none");
4157             }
4158             else if( nGroupIndex >= 0 ) {
4159               int w;
4160               char* pszItems = NULL;
4161               for(w=0;w<groupList->groups[nGroupIndex].numitems;w++) {
4162                 if( w > 0 )
4163                   pszItems = msStringConcatenate(pszItems, ",");
4164                 pszItems = msStringConcatenate(pszItems, groupList->groups[nGroupIndex].items[w]);
4165               }
4166               pszGMLGroups = msStrdup(groupList->groups[nGroupIndex].name);
4167               pszGMLIncludeItems = msStrdup(pszItems);
4168               pszGMLGeometries = msStrdup("none");
4169               msFree(pszItems);
4170             }
4171             else {
4172               pszGMLGroups = msStrdup("");
4173 
4174               /* Check that the property name is allowed (#3563) */
4175               /*we need to check of the property name is the geometry name; In that case it
4176                     is a valid property name*/
4177               if (!msWFSIsAllowedItem(itemList, pszValueReference)) {
4178                 for(z=0; z<geometryList->numgeometries; z++) {
4179                   if (strcasecmp(pszValueReference, geometryList->geometries[z].name) == 0)
4180                     break;
4181                 }
4182 
4183                 if (z == geometryList->numgeometries) {
4184                   msSetError(MS_WFSERR,
4185                             "Invalid VALUEREFERENCE %s",  "msWFSGetPropertyValue()", paramsObj->pszValueReference);
4186                   msFree(pszGMLGroups);
4187                   msGMLFreeItems(itemList);
4188                   msGMLFreeGroups(groupList);
4189                   msGMLFreeGeometries(geometryList);
4190                   return msWFSException(map, "valuereference", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
4191                 }
4192                 else {
4193                   pszGMLIncludeItems = msStrdup("");
4194                   pszGMLGeometries = msStrdup(pszValueReference);
4195                 }
4196               }
4197               else {
4198                 pszGMLIncludeItems = msStrdup(pszValueReference);
4199                 pszGMLGeometries = msStrdup("none");
4200               }
4201             }
4202 
4203             msLayerClose(lp);
4204 
4205             msGMLFreeItems(itemList);
4206             msGMLFreeGroups(groupList);
4207             msGMLFreeGeometries(geometryList);
4208         }
4209   }
4210   else {
4211         /* Requested layer name was not in capabilities:
4212          * either it just doesn't exist
4213          */
4214         msSetError(MS_WFSERR,
4215                     "TYPENAME '%s' doesn't exist in this server.  Please check the capabilities and reformulate your request.",
4216                     "msWFSGetFeature()", paramsObj->pszTypeName);
4217         msFree(pszGMLGroups);
4218         msFree(pszGMLIncludeItems);
4219         msFree(pszGMLGeometries);
4220         return msWFSException(map, "typename", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
4221   }
4222 
4223   /* Initialize gml options */
4224   msWFSInitGMLInfo(&gmlinfo);
4225 
4226   if (paramsObj->pszResultType != NULL) {
4227     if (strcasecmp(paramsObj->pszResultType, "hits") == 0)
4228       iResultTypeHits = 1;
4229   }
4230 
4231   gmlinfo.typename = paramsObj->pszTypeName;
4232 
4233   /* Validate outputformat */
4234   status = msWFSGetGMLOutputFormat(map, paramsObj, &gmlinfo, nWFSVersion);
4235   if( status < 0 ) {
4236     msSetError(MS_WFSERR,
4237                 "'%s' is not a permitted output format for GetPropertyValue request.",
4238                 "msWFSGetPropertyValue()",
4239                 paramsObj->pszOutputFormat);
4240     msFree(pszGMLGroups);
4241     msFree(pszGMLIncludeItems);
4242     msFree(pszGMLGeometries);
4243     return msWFSException(map, "outputformat",
4244                         MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4245                         paramsObj->pszVersion );
4246   }
4247   else {
4248     outputformat = (OWSGMLVersion) status;
4249   }
4250 
4251   msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, iResultTypeHits,
4252                                         &maxfeatures, &startindex);
4253 
4254   status = msWFSAnalyzeBBOX(map, paramsObj, &bbox, &sBBoxSrs);
4255   if( status != 0 ) {
4256       msFree(pszGMLGroups);
4257       msFree(pszGMLIncludeItems);
4258       msFree(pszGMLGeometries);
4259       return status;
4260       }
4261 
4262   if( iResultTypeHits == 1 )
4263   {
4264       map->query.only_cache_result_count = MS_TRUE;
4265   }
4266   else
4267   {
4268       msWFSSetShapeCache(map);
4269   }
4270 
4271   status = msWFSRetrieveFeatures(map,
4272                                  ows_request,
4273                            paramsObj,
4274                            &gmlinfo,
4275                            paramsObj->pszFilter,
4276                            paramsObj->pszBbox != NULL,
4277                            sBBoxSrs,
4278                            bbox,
4279                            paramsObj->pszFeatureId,
4280                            (char**) &pszTypeName,
4281                            1,
4282                            maxfeatures,
4283                            nWFSVersion,
4284                            &iNumberOfFeatures,
4285                            &bHasNextFeatures);
4286   if( status != MS_SUCCESS )
4287   {
4288       msFree(sBBoxSrs);
4289       msFree(pszGMLGroups);
4290       msFree(pszGMLIncludeItems);
4291       msFree(pszGMLGeometries);
4292       return status;
4293   }
4294 
4295   /* ----------------------------------------- */
4296   /* Now compute nMatchingFeatures for WFS 2.0 */
4297   /* ----------------------------------------- */
4298 
4299   nMatchingFeatures = msWFSComputeMatchingFeatures(map,ows_request,paramsObj,
4300                                                    iNumberOfFeatures,
4301                                                    maxfeatures,
4302                                                    &gmlinfo,
4303                                                    bbox,
4304                                                    sBBoxSrs,
4305                                                    (char**) &pszTypeName,
4306                                                    1,
4307                                                    nWFSVersion);
4308 
4309   msFree(sBBoxSrs);
4310   sBBoxSrs = NULL;
4311 
4312   /*
4313   ** GML Header generation.
4314   */
4315   msIO_setHeader("Content-Type","%s; charset=UTF-8", gmlinfo.output_mime_type);
4316   msIO_sendHeaders();
4317 
4318   status = msWFSGetFeature_GMLPreamble( map, req, &gmlinfo, paramsObj,
4319                                  outputformat,
4320                                  iResultTypeHits,
4321                                  iNumberOfFeatures,
4322                                  nMatchingFeatures,
4323                                  maxfeatures,
4324                                  bHasNextFeatures,
4325                                  nWFSVersion );
4326 
4327   if(status == MS_SUCCESS && maxfeatures != 0 && iResultTypeHits == 0)
4328   {
4329       if( pszGMLGroups )
4330         msInsertHashTable(&(lp->metadata), "GML_GROUPS", pszGMLGroups);
4331       if( pszGMLIncludeItems )
4332         msInsertHashTable(&(lp->metadata), "GML_INCLUDE_ITEMS", pszGMLIncludeItems);
4333       if( pszGMLGeometries )
4334         msInsertHashTable(&(lp->metadata), "GML_GEOMETRIES", pszGMLGeometries);
4335 
4336       status = msGMLWriteWFSQuery(map, stdout,
4337                                   gmlinfo.user_namespace_prefix,
4338                                   outputformat,
4339                                   nWFSVersion,
4340                                   MS_TRUE,
4341                                   MS_TRUE);
4342   }
4343 
4344   msIO_printf("</wfs:ValueCollection>\n");
4345 
4346   msFree(pszGMLGroups);
4347   msFree(pszGMLIncludeItems);
4348   msFree(pszGMLGeometries);
4349 
4350   free(gmlinfo.script_url);
4351   free(gmlinfo.script_url_encoded);
4352   msFree(gmlinfo.user_namespace_uri_encoded);
4353 
4354   return status;
4355 }
4356 
4357 
4358 #endif /* USE_WFS_SVR */
4359 
4360 
4361 /*
4362 ** msWFSDispatch() is the entry point for WFS requests.
4363 ** - If this is a valid request then it is processed and MS_SUCCESS is returned
4364 **   on success, or MS_FAILURE on failure.
4365 ** - If this does not appear to be a valid WFS request then MS_DONE
4366 **   is returned and MapServer is expected to process this as a regular
4367 **   MapServer request.
4368 */
msWFSDispatch(mapObj * map,cgiRequestObj * requestobj,owsRequestObj * ows_request,int force_wfs_mode)4369 int msWFSDispatch(mapObj *map, cgiRequestObj *requestobj, owsRequestObj *ows_request, int force_wfs_mode)
4370 {
4371 #ifdef USE_WFS_SVR
4372   int status;
4373   int returnvalue = MS_DONE;
4374   int nWFSVersion;
4375   char* validated_language;
4376 
4377   /* static char *wmtver = NULL, *request=NULL, *service=NULL; */
4378   wfsParamsObj *paramsObj;
4379   /*
4380   ** Populate the Params object based on the request
4381   */
4382   paramsObj = msWFSCreateParamsObj();
4383   /* TODO : store also parameters that are inside the map object */
4384   /* into the paramsObj.  */
4385   status = msWFSParseRequest(map, requestobj, ows_request, paramsObj, force_wfs_mode);
4386   if (status != MS_SUCCESS)
4387   {
4388     msWFSFreeParamsObj(paramsObj);
4389     return status;
4390   }
4391 
4392   validated_language = msOWSGetLanguageFromList(map, "FO", paramsObj->pszLanguage);
4393   if (validated_language != NULL) {
4394     msMapSetLanguageSpecificConnection(map, validated_language);
4395   }
4396   msFree(validated_language);
4397 
4398   if (force_wfs_mode) {
4399     /*request is always required*/
4400     if (paramsObj->pszRequest==NULL || strlen(paramsObj->pszRequest)<=0) {
4401       msSetError(MS_WFSERR,
4402                  "Incomplete WFS request: REQUEST parameter missing",
4403                  "msWFSDispatch()");
4404       returnvalue = msWFSException(map, "request", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
4405       msWFSFreeParamsObj(paramsObj);
4406       return returnvalue;
4407     }
4408 
4409     /*version:
4410       wfs 1.0 and 1.1.0 POST request: optional
4411       wfs 1.0 and 1.1.0 GET request: optional for getcapabilities and required for describefeature and getfeature
4412      */
4413     if (paramsObj->pszVersion == NULL && requestobj && requestobj->postrequest == NULL &&
4414         strcasecmp(paramsObj->pszRequest, "GetCapabilities") != 0) {
4415       msSetError(MS_WFSERR,
4416                  "Invalid WFS request: VERSION parameter missing",
4417                  "msWFSDispatch()");
4418       returnvalue = msWFSException(map, "version", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
4419       msWFSFreeParamsObj(paramsObj);
4420       return returnvalue;
4421     }
4422 
4423     if (paramsObj->pszVersion == NULL || strlen(paramsObj->pszVersion) <=0)
4424       paramsObj->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
4425 
4426     /*service
4427       wfs 1.0 and 1.1.0 GET: required and should be set to WFS
4428       wfs 1.0 POST: required
4429       wfs 1.1.1 POST: optional
4430     */
4431     if ((paramsObj->pszService == NULL || strlen(paramsObj->pszService) == 0) &&
4432         ((requestobj && requestobj->postrequest == NULL) || strcasecmp(paramsObj->pszVersion,"1.0") ==0)) {
4433       msSetError(MS_WFSERR,
4434                  "Invalid WFS request: Missing SERVICE parameter",
4435                  "msWFSDispatch()");
4436       returnvalue = msWFSException(map, "service", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
4437       msWFSFreeParamsObj(paramsObj);
4438       return returnvalue;
4439     }
4440 
4441     if (paramsObj->pszService == NULL || strlen(paramsObj->pszService) == 0)
4442       paramsObj->pszService = msStrdup("WFS");
4443 
4444     if (paramsObj->pszService!=NULL && strcasecmp(paramsObj->pszService, "WFS") != 0) {
4445       msSetError(MS_WFSERR,
4446                  "Invalid WFS request: SERVICE parameter must be set to WFS",
4447                  "msWFSDispatch()");
4448       returnvalue = msWFSException(map, "service", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
4449       msWFSFreeParamsObj(paramsObj);
4450       return returnvalue;
4451     }
4452 
4453   }
4454   /* If SERVICE is specified then it MUST be "WFS" */
4455   if (paramsObj->pszService != NULL &&
4456       strcasecmp(paramsObj->pszService, "WFS") != 0) {
4457     msWFSFreeParamsObj(paramsObj);
4458     return MS_DONE;  /* Not a WFS request */
4459   }
4460 
4461   /* If SERVICE, VERSION and REQUEST not included than this isn't a WFS req*/
4462   if (paramsObj->pszService == NULL && paramsObj->pszVersion==NULL &&
4463       paramsObj->pszRequest==NULL) {
4464     msWFSFreeParamsObj(paramsObj);
4465     return MS_DONE;  /* Not a WFS request */
4466   }
4467 
4468   /* VERSION *and* REQUEST *and* SERVICE required by all WFS requests including
4469    * GetCapabilities.
4470    */
4471   if (paramsObj->pszVersion==NULL || strlen(paramsObj->pszVersion)<=0) {
4472     msSetError(MS_WFSERR,
4473                "Incomplete WFS request: VERSION parameter missing",
4474                "msWFSDispatch()");
4475 
4476     returnvalue = msWFSException11(map, "version", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, msWFSGetDefaultVersion(map));
4477     msWFSFreeParamsObj(paramsObj);
4478     return returnvalue;
4479   }
4480 
4481   if (paramsObj->pszRequest==NULL || strlen(paramsObj->pszRequest)<=0) {
4482     msSetError(MS_WFSERR,
4483                "Incomplete WFS request: REQUEST parameter missing",
4484                "msWFSDispatch()");
4485     returnvalue = msWFSException(map, "request", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
4486     msWFSFreeParamsObj(paramsObj);
4487     return returnvalue;
4488   }
4489 
4490   if (paramsObj->pszService==NULL || strlen(paramsObj->pszService)<=0) {
4491     msSetError(MS_WFSERR,
4492                "Incomplete WFS request: SERVICE parameter missing",
4493                "msWFSDispatch()");
4494 
4495     returnvalue = msWFSException(map, "service", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
4496     msWFSFreeParamsObj(paramsObj);
4497     return returnvalue;
4498   }
4499 
4500   if ((status = msOWSMakeAllLayersUnique(map)) != MS_SUCCESS) {
4501     msSetError(MS_WFSERR, "msOWSMakeAllLayersUnique() failed", "msWFSDispatch()");
4502     returnvalue = msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
4503     msWFSFreeParamsObj(paramsObj);
4504     return returnvalue;
4505   }
4506   /*
4507   ** Start dispatching requests
4508   */
4509   if (strcasecmp(paramsObj->pszRequest, "GetCapabilities") == 0 ) {
4510     msOWSRequestLayersEnabled(map, "F", paramsObj->pszRequest, ows_request);
4511     if (ows_request->numlayers == 0) {
4512       msSetError(MS_WFSERR, "WFS request not enabled. Check wfs/ows_enable_request settings.", "msWFSDispatch()");
4513       returnvalue = msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
4514       msWFSFreeParamsObj(paramsObj);
4515       return returnvalue;
4516     }
4517 
4518     returnvalue = msWFSGetCapabilities(map, paramsObj, requestobj, ows_request);
4519     msWFSFreeParamsObj(paramsObj);
4520     return returnvalue;
4521   }
4522 
4523   /*
4524   ** Validate VERSION against the versions that we support... we don't do this
4525   ** for GetCapabilities in order to allow version negociation.
4526   */
4527   if (strcmp(paramsObj->pszVersion, "1.0.0") != 0 &&
4528       strcmp(paramsObj->pszVersion, "1.1.0") != 0 &&
4529       strcmp(paramsObj->pszVersion, "2.0.0") != 0) {
4530     msSetError(MS_WFSERR,
4531                "WFS Server does not support VERSION %s.",
4532                "msWFSDispatch()", paramsObj->pszVersion);
4533     returnvalue = msWFSException(map, "version", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,msWFSGetDefaultVersion(map));
4534     msWFSFreeParamsObj(paramsObj);
4535     return returnvalue;
4536 
4537   }
4538 
4539   nWFSVersion = msOWSParseVersionString(paramsObj->pszVersion);
4540 
4541   /* Since the function can still return MS_DONE, we add an extra service check to not call
4542      msOWSRequestLayersEnabled twice */
4543   if (strcasecmp(paramsObj->pszService, "WFS") == 0) {
4544     msOWSRequestLayersEnabled(map, "F", paramsObj->pszRequest, ows_request);
4545     if (ows_request->numlayers == 0) {
4546       msSetError(MS_WFSERR, "WFS request not enabled. Check wfs/ows_enable_request settings.", "msWFSDispatch()");
4547       returnvalue = msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
4548       msWFSFreeParamsObj(paramsObj);
4549       return returnvalue;
4550     }
4551   }
4552 
4553   returnvalue = MS_DONE;
4554   /* Continue dispatching...
4555    */
4556   if (strcasecmp(paramsObj->pszRequest, "DescribeFeatureType") == 0)
4557     returnvalue = msWFSDescribeFeatureType(map, paramsObj, ows_request, nWFSVersion);
4558 
4559   else if (strcasecmp(paramsObj->pszRequest, "GetFeature") == 0)
4560     returnvalue = msWFSGetFeature(map, paramsObj, requestobj, ows_request, nWFSVersion);
4561 
4562   else if (nWFSVersion >= OWS_2_0_0 &&
4563            strcasecmp(paramsObj->pszRequest, "GetPropertyValue") == 0)
4564     returnvalue = msWFSGetPropertyValue(map, paramsObj, requestobj, ows_request, nWFSVersion);
4565 
4566   else if (nWFSVersion >= OWS_2_0_0 &&
4567            strcasecmp(paramsObj->pszRequest, "ListStoredQueries") == 0)
4568     returnvalue = msWFSListStoredQueries20(map, paramsObj, requestobj, ows_request);
4569 
4570   else if (nWFSVersion >= OWS_2_0_0 &&
4571            strcasecmp(paramsObj->pszRequest, "DescribeStoredQueries") == 0)
4572     returnvalue = msWFSDescribeStoredQueries20(map, paramsObj, requestobj, ows_request);
4573 
4574   else if (msWFSGetIndexUnsupportedOperation(paramsObj->pszRequest) >= 0 ) {
4575     /* Unsupported WFS request */
4576     msSetError(MS_WFSERR, "Unsupported WFS request: %s", "msWFSDispatch()",
4577                paramsObj->pszRequest);
4578     returnvalue = msWFSException(map, paramsObj->pszRequest, MS_OWS_ERROR_OPERATION_NOT_SUPPORTED, paramsObj->pszVersion);
4579   } else if (strcasecmp(paramsObj->pszService, "WFS") == 0) {
4580     /* Invalid WFS request */
4581     msSetError(MS_WFSERR, "Invalid WFS request: %s", "msWFSDispatch()",
4582                paramsObj->pszRequest);
4583     returnvalue = msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
4584   }
4585 
4586   /* This was not detected as a WFS request... let MapServer handle it */
4587   msWFSFreeParamsObj(paramsObj);
4588   return returnvalue;
4589 
4590 #else
4591   msSetError(MS_WFSERR, "WFS server support is not available.",
4592              "msWFSDispatch()");
4593   return(MS_FAILURE);
4594 #endif
4595 }
4596 
4597 /************************************************************************/
4598 /*                           msWFSCreateParamsObj                       */
4599 /*                                                                      */
4600 /*      Create a parameter object, initialize it.                       */
4601 /*      The caller should free the object using msWFSFreeParamsObj.     */
4602 /************************************************************************/
msWFSCreateParamsObj()4603 wfsParamsObj *msWFSCreateParamsObj()
4604 {
4605   wfsParamsObj *paramsObj = (wfsParamsObj *)calloc(1, sizeof(wfsParamsObj));
4606   MS_CHECK_ALLOC(paramsObj, sizeof(wfsParamsObj), NULL);
4607 
4608   paramsObj->nMaxFeatures = -1;
4609   paramsObj->nStartIndex = -1;
4610 
4611   return paramsObj;
4612 }
4613 
4614 
4615 
4616 /************************************************************************/
4617 /*                            msWFSFreeParmsObj                         */
4618 /*                                                                      */
4619 /*      Free params object.                                             */
4620 /************************************************************************/
msWFSFreeParamsObj(wfsParamsObj * wfsparams)4621 void msWFSFreeParamsObj(wfsParamsObj *wfsparams)
4622 {
4623   if (wfsparams) {
4624     free(wfsparams->pszVersion);
4625     free(wfsparams->pszUpdateSequence);
4626     free(wfsparams->pszRequest);
4627     free(wfsparams->pszService);
4628     free(wfsparams->pszTypeName);
4629     free(wfsparams->pszFilter);
4630     free(wfsparams->pszFilterLanguage);
4631     free(wfsparams->pszBbox);
4632     free(wfsparams->pszGeometryName);
4633     free(wfsparams->pszOutputFormat);
4634     free(wfsparams->pszFeatureId);
4635     free(wfsparams->pszSrs);
4636     free(wfsparams->pszResultType);
4637     free(wfsparams->pszPropertyName);
4638     free(wfsparams->pszAcceptVersions);
4639     free(wfsparams->pszSections);
4640     free(wfsparams->pszSortBy);
4641     free(wfsparams->pszLanguage);
4642     free(wfsparams->pszValueReference);
4643     free(wfsparams->pszStoredQueryId);
4644 
4645     free(wfsparams);
4646   }
4647 }
4648 
4649 #ifdef USE_WFS_SVR
4650 /************************************************************************/
4651 /*                       msWFSGetDefaultVersion                         */
4652 /************************************************************************/
4653 static
msWFSGetDefaultVersion(mapObj * map)4654 const char *msWFSGetDefaultVersion(mapObj *map)
4655 {
4656   const char* pszVersion =
4657     msOWSLookupMetadata(&(map->web.metadata), "F", "getcapabilities_version");
4658   if( pszVersion != NULL )
4659   {
4660       /* Check that the metadata value is one of the recognized versions */
4661       int iVersion = msOWSParseVersionString(pszVersion);
4662       int i;
4663       for( i = 0; i < wfsNumSupportedVersions; i ++ )
4664       {
4665           if( iVersion == wfsSupportedVersions[i] )
4666               return wfsSupportedVersionsStr[i];
4667       }
4668       /* If not, use the default (= latest) version */
4669       msDebug("msWFSGetDefaultVersion(): invalid value for "
4670               "wfs_getcapabilities_version: %s\n", pszVersion);
4671   }
4672   return WFS_LATEST_VERSION;
4673 }
4674 
4675 /************************************************************************/
4676 /*                             msWFSSetParam                            */
4677 /************************************************************************/
msWFSSetParam(char ** ppszOut,cgiRequestObj * request,int i,const char * pszExpectedParamName)4678 static int msWFSSetParam(char** ppszOut, cgiRequestObj *request, int i,
4679                          const char* pszExpectedParamName)
4680 {
4681     if( strcasecmp(request->ParamNames[i], pszExpectedParamName) == 0 )
4682     {
4683         if( *ppszOut == NULL )
4684             *ppszOut = msStrdup(request->ParamValues[i]);
4685         return 1;
4686     }
4687     return 0;
4688 }
4689 
4690 /************************************************************************/
4691 /*                         msWFSParseXMLQueryNode                       */
4692 /************************************************************************/
4693 
msWFSParseXMLQueryNode(CPLXMLNode * psQuery,wfsParamsObj * wfsparams)4694 static void msWFSParseXMLQueryNode(CPLXMLNode* psQuery, wfsParamsObj *wfsparams)
4695 {
4696     const char* pszTypename;
4697     const char* pszValue;
4698     char *pszSerializedFilter, *pszTmp, *pszTmp2;
4699     CPLXMLNode* psPropertyName;
4700     CPLXMLNode* psFilter;
4701     CPLXMLNode* psSortBy;
4702     char* pszSortBy = NULL;
4703 
4704     pszValue = CPLGetXMLValue(psQuery,  "srsName",
4705                                 NULL);
4706     if (pszValue)
4707     {
4708         msFree(wfsparams->pszSrs);
4709         wfsparams->pszSrs = msStrdup(pszValue);
4710     }
4711 
4712     /* parse typenames */
4713     pszTypename = CPLGetXMLValue(psQuery,
4714                                 "typename", NULL);
4715     if( pszTypename == NULL ) /* WFS 2.0 */
4716         pszTypename = CPLGetXMLValue(psQuery,
4717                                 "typeNames", NULL);
4718 
4719     /*parse property name*/
4720     psPropertyName = CPLGetXMLNode(psQuery, "PropertyName");
4721     pszTmp2= NULL;
4722 
4723     /*when there is no PropertyName, we output (!) so that it is parsed properly in GetFeature*/
4724     if (psPropertyName == NULL)
4725         pszTmp2 = msStrdup("!");
4726 
4727     while (psPropertyName) {
4728         if (!psPropertyName->pszValue ||
4729             strcasecmp(psPropertyName->pszValue, "PropertyName") != 0) {
4730             psPropertyName = psPropertyName->psNext;
4731         continue;
4732         }
4733         pszValue = CPLGetXMLValue(psPropertyName, NULL, NULL);
4734         if (pszTmp2 == NULL) {
4735             pszTmp2 = msStrdup(pszValue);
4736         } else {
4737             pszTmp = msStrdup(pszTmp2);
4738             pszTmp2 = (char *)msSmallRealloc(pszTmp2, sizeof(char)* (strlen(pszTmp)+ strlen(pszValue)+2));
4739             sprintf(pszTmp2,"%s,%s",pszTmp, pszValue);
4740             msFree(pszTmp);
4741         }
4742         psPropertyName = psPropertyName->psNext;
4743     }
4744     if (pszTmp2) {
4745         pszTmp = msStrdup(pszTmp2);
4746         pszTmp2 = (char *)msSmallRealloc(pszTmp2, sizeof(char)* (strlen(pszTmp)+ 3));
4747         sprintf(pszTmp2,"(%s)", pszTmp);
4748         msFree(pszTmp);
4749 
4750         msWFSBuildParamList(&(wfsparams->pszPropertyName), pszTmp2, "");
4751         msFree(pszTmp2);
4752         pszTmp2 = NULL;
4753     }
4754 
4755     /* parse filter */
4756     psFilter = CPLGetXMLNode(psQuery, "Filter");
4757 
4758     if (psFilter == NULL) {
4759         /*when there is no Filter, we output (!) so that it is parsed properly in GetFeature*/
4760         pszSerializedFilter = msStrdup("(!)");
4761     } else {
4762         char* pszCPLTmp = CPLSerializeXMLTree(psFilter);
4763         pszSerializedFilter = (char *)msSmallMalloc(sizeof(char)*
4764                                         (strlen(pszCPLTmp)+3));
4765         sprintf(pszSerializedFilter, "(%s)", pszCPLTmp);
4766         CPLFree(pszCPLTmp);
4767     }
4768 
4769     msWFSBuildParamList(&(wfsparams->pszFilter), pszSerializedFilter, "");
4770     free(pszSerializedFilter);
4771 
4772     /* parse SortBy */
4773     psSortBy = CPLGetXMLNode(psQuery, "SortBy");
4774     if( psSortBy != NULL )
4775     {
4776         int bFirstProperty = MS_TRUE;
4777         CPLXMLNode* psIter = psSortBy->psChild;
4778 
4779         pszSortBy = msStringConcatenate(pszSortBy, "(");
4780 
4781         while( psIter != NULL )
4782         {
4783             if( psIter->eType == CXT_Element &&
4784                 strcmp(psIter->pszValue, "SortProperty") == 0 )
4785             {
4786                 const char* pszPropertyName = CPLGetXMLValue(psIter, "PropertyName", NULL);
4787                 if( pszPropertyName == NULL )
4788                     pszPropertyName = CPLGetXMLValue(psIter, "ValueReference", NULL);
4789                 if( pszPropertyName != NULL )
4790                 {
4791                     const char* pszSortOrder = CPLGetXMLValue(psIter, "SortOrder", NULL);
4792                     if( !bFirstProperty )
4793                         pszSortBy = msStringConcatenate(pszSortBy, ",");
4794                     bFirstProperty = MS_FALSE;
4795 
4796                     pszSortBy = msStringConcatenate(pszSortBy, pszPropertyName);
4797                     if( pszSortOrder != NULL )
4798                     {
4799                         pszSortBy = msStringConcatenate(pszSortBy, " ");
4800                         pszSortBy = msStringConcatenate(pszSortBy, pszSortOrder);
4801                     }
4802                 }
4803             }
4804             psIter = psIter->psNext;
4805         }
4806 
4807         pszSortBy = msStringConcatenate(pszSortBy, ")");
4808     }
4809     else
4810     {
4811         pszSortBy = msStrdup("(!)");
4812     }
4813 
4814     msWFSBuildParamList(&(wfsparams->pszSortBy), pszSortBy, "");
4815     free(pszSortBy);
4816 
4817     /* Special case for "urn:ogc:def:query:OGC-WFS::GetFeatureById" */
4818     /* Resolve the property typename */
4819     if( pszTypename != NULL && strcmp(pszTypename, "?") == 0 && psFilter != NULL )
4820     {
4821         const char* rid;
4822         rid = CPLGetXMLValue(psFilter, "ResourceId.rid", NULL);
4823         if( rid != NULL )
4824         {
4825             char* pszTmpTypename = msStrdup(rid);
4826             char* pszDot = strchr(pszTmpTypename, '.');
4827             if( pszDot )
4828             {
4829                 *pszDot = '\0';
4830                 msWFSBuildParamList(&(wfsparams->pszTypeName), pszTmpTypename, ",");
4831                 pszTypename = NULL;
4832 
4833                 if( wfsparams->countGetFeatureById >= 0 )
4834                     wfsparams->countGetFeatureById ++;
4835             }
4836             else
4837                 wfsparams->countGetFeatureById = -1;
4838             msFree(pszTmpTypename);
4839         }
4840         else
4841             wfsparams->countGetFeatureById = -1;
4842     }
4843     else
4844         wfsparams->countGetFeatureById = -1;
4845 
4846     if (pszTypename) {
4847         msWFSBuildParamList(&(wfsparams->pszTypeName), pszTypename, ",");
4848     }
4849 
4850 }
4851 
4852 /************************************************************************/
4853 /*                     msWFSAnalyzeStoredQuery                          */
4854 /************************************************************************/
4855 
msWFSAnalyzeStoredQuery(mapObj * map,wfsParamsObj * wfsparams,const char * id,const char * pszResolvedQuery)4856 static int msWFSAnalyzeStoredQuery(mapObj* map,
4857                                    wfsParamsObj *wfsparams,
4858                                    const char* id,
4859                                    const char* pszResolvedQuery)
4860 {
4861     CPLXMLNode* psRoot;
4862     CPLXMLNode* psQuery;
4863     CPLXMLNode* psIter;
4864 
4865     psRoot = CPLParseXMLString(pszResolvedQuery);
4866 
4867     if( psRoot == NULL )
4868     {
4869         msSetError(MS_WFSERR, "Resolved definition for stored query '%s' is invalid", "msWFSParseRequest()", id);
4870         msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, wfsparams->pszVersion);
4871         return MS_FAILURE;
4872     }
4873 
4874     CPLStripXMLNamespace(psRoot, NULL, 1);
4875     psQuery = CPLGetXMLNode(psRoot, "=StoredQueryDescription.QueryExpressionText.Query");
4876     if( psQuery == NULL )
4877     {
4878         msSetError(MS_WFSERR, "Resolved definition for stored query '%s' is invalid", "msWFSParseRequest()", id);
4879         msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, wfsparams->pszVersion);
4880         CPLDestroyXMLNode(psRoot);
4881         return MS_FAILURE;
4882     }
4883 
4884     psIter = psQuery;
4885     while( psIter != NULL )
4886     {
4887         if( psIter->eType == CXT_Element && strcmp(psIter->pszValue, "Query") == 0 ) {
4888             msWFSParseXMLQueryNode(psIter, wfsparams);
4889         }
4890         psIter = psIter->psNext;
4891     }
4892 
4893     CPLDestroyXMLNode(psRoot);
4894 
4895     return MS_SUCCESS;
4896 }
4897 
4898 /************************************************************************/
4899 /*                     msWFSParseXMLStoredQueryNode                     */
4900 /************************************************************************/
4901 
msWFSParseXMLStoredQueryNode(mapObj * map,wfsParamsObj * wfsparams,CPLXMLNode * psQuery)4902 static int msWFSParseXMLStoredQueryNode(mapObj* map,
4903                                         wfsParamsObj *wfsparams,
4904                                         CPLXMLNode* psQuery)
4905 {
4906     const char* id;
4907     CPLXMLNode* psIter;
4908     hashTableObj * hashTable;
4909     char* pszResolvedQuery;
4910     int status;
4911 
4912     id = CPLGetXMLValue(psQuery, "id", NULL);
4913     if( id == NULL )
4914     {
4915         msSetError(MS_WFSERR, "Missing 'id' attribute in StoredQuery", "msWFSParseRequest()");
4916         return msWFSException(map, "id", MS_OWS_ERROR_MISSING_PARAMETER_VALUE, wfsparams->pszVersion);
4917     }
4918 
4919     hashTable = msCreateHashTable();
4920     psIter = psQuery->psChild;
4921     while(psIter != NULL)
4922     {
4923         if( psIter->eType == CXT_Element &&
4924             strcmp(psIter->pszValue, "Parameter") == 0 )
4925         {
4926             const char* name;
4927             CPLXMLNode* psIter2;
4928             char* pszValue;
4929 
4930             name = CPLGetXMLValue(psIter, "name", NULL);
4931             if( name == NULL )
4932             {
4933                 msSetError(MS_WFSERR, "Missing 'name' attribute in Parameter of StoredQuery", "msWFSParseRequest()");
4934                 msFreeHashTable(hashTable);
4935                 return msWFSException(map,  NULL, MS_OWS_ERROR_INVALID_PARAMETER_VALUE, wfsparams->pszVersion);
4936             }
4937 
4938             psIter2 = psIter->psChild;
4939             while( psIter2 != NULL )
4940             {
4941                 if( psIter2->eType != CXT_Attribute )
4942                     break;
4943                 psIter2 = psIter2->psNext;
4944             }
4945 
4946             pszValue = CPLSerializeXMLTree(psIter2);
4947             msInsertHashTable(hashTable, name, pszValue);
4948             CPLFree(pszValue);
4949         }
4950         psIter = psIter->psNext;
4951     }
4952 
4953     pszResolvedQuery = msWFSGetResolvedStoredQuery20(map, wfsparams, id, hashTable);
4954     msFreeHashTable(hashTable);
4955     if( pszResolvedQuery == NULL)
4956         return MS_FAILURE;
4957 
4958     status = msWFSAnalyzeStoredQuery(map, wfsparams, id, pszResolvedQuery);
4959 
4960     msFree(pszResolvedQuery);
4961 
4962     return status;
4963 }
4964 
4965 /************************************************************************/
4966 /*                    msWFSSimplifyPropertyNameAndFilter                */
4967 /************************************************************************/
4968 
msWFSSimplifyPropertyNameAndFilter(wfsParamsObj * wfsparams)4969 static void msWFSSimplifyPropertyNameAndFilter(wfsParamsObj *wfsparams)
4970 {
4971     int i;
4972 
4973     /* If no PropertyName at all was specified, then clear the field */
4974     /* that will be easier to generate valid 'next' and 'previous' uri */
4975     /* for WFS 2.0 GetFeature */
4976     if( wfsparams->pszPropertyName != NULL )
4977     {
4978         i = 0;
4979         while( strncmp(wfsparams->pszPropertyName + i, "(!)", 3) == 0 )
4980             i += 3;
4981         if( wfsparams->pszPropertyName[i] == '\0' )
4982         {
4983             msFree(wfsparams->pszPropertyName);
4984             wfsparams->pszPropertyName = NULL;
4985         }
4986     }
4987 
4988     /* Same with filter */
4989     if( wfsparams->pszFilter != NULL )
4990     {
4991         i = 0;
4992         while( strncmp(wfsparams->pszFilter + i, "(!)", 3) == 0 )
4993             i += 3;
4994         if( wfsparams->pszFilter[i] == '\0' )
4995         {
4996             msFree(wfsparams->pszFilter);
4997             wfsparams->pszFilter = NULL;
4998         }
4999     }
5000 
5001     /* Same with SortBy */
5002     if( wfsparams->pszSortBy != NULL )
5003     {
5004         i = 0;
5005         while( strncmp(wfsparams->pszSortBy + i, "(!)", 3) == 0 )
5006             i += 3;
5007         if( wfsparams->pszSortBy[i] == '\0' )
5008         {
5009             msFree(wfsparams->pszSortBy);
5010             wfsparams->pszSortBy = NULL;
5011         }
5012     }
5013 }
5014 
5015 #endif /* USE_WFS_SVR */
5016 
5017 
5018 /************************************************************************/
5019 /*                            msWFSParseRequest                         */
5020 /*                                                                      */
5021 /*      Parse request into the params object.                           */
5022 /************************************************************************/
msWFSParseRequest(mapObj * map,cgiRequestObj * request,owsRequestObj * ows_request,wfsParamsObj * wfsparams,int force_wfs_mode)5023 int msWFSParseRequest(mapObj *map, cgiRequestObj *request, owsRequestObj *ows_request,
5024                       wfsParamsObj *wfsparams, int force_wfs_mode)
5025 {
5026 #ifdef USE_WFS_SVR
5027   int i = 0;
5028 
5029   if (!request || !wfsparams)
5030     return msWFSException(map, "request", "InvalidRequest", NULL);
5031 
5032   if (request->NumParams > 0 && request->postrequest == NULL ) {
5033     for(i=0; i<request->NumParams; i++) {
5034       if (request->ParamNames[i] && request->ParamValues[i]) {
5035         if( msWFSSetParam(&(wfsparams->pszVersion), request, i, "VERSION") )
5036             ;
5037 
5038         else if( msWFSSetParam(&(wfsparams->pszUpdateSequence), request, i, "UPDATESEQUENCE") )
5039             ;
5040 
5041         else if( msWFSSetParam(&(wfsparams->pszRequest), request, i, "REQUEST") )
5042             ;
5043 
5044         else if( msWFSSetParam(&(wfsparams->pszService), request, i, "SERVICE") )
5045             ;
5046 
5047         else if (strcasecmp(request->ParamNames[i], "MAXFEATURES") == 0)
5048           wfsparams->nMaxFeatures = atoi(request->ParamValues[i]);
5049 
5050         /* WFS 2.0 */
5051         else if (strcasecmp(request->ParamNames[i], "COUNT") == 0)
5052           wfsparams->nMaxFeatures = atoi(request->ParamValues[i]);
5053 
5054         else if (strcasecmp(request->ParamNames[i], "STARTINDEX") == 0)
5055           wfsparams->nStartIndex = atoi(request->ParamValues[i]);
5056 
5057         else if( msWFSSetParam(&(wfsparams->pszBbox), request, i, "BBOX") )
5058             ;
5059 
5060         else if( msWFSSetParam(&(wfsparams->pszSrs), request, i, "SRSNAME") )
5061             ;
5062 
5063         else if( msWFSSetParam(&(wfsparams->pszResultType), request, i, "RESULTTYPE") )
5064             ;
5065 
5066         else if( msWFSSetParam(&(wfsparams->pszTypeName), request, i, "TYPENAME") )
5067             ;
5068 
5069         /* WFS 2.0 */
5070         else if( msWFSSetParam(&(wfsparams->pszTypeName), request, i, "TYPENAMES") )
5071             ;
5072 
5073         else if( msWFSSetParam(&(wfsparams->pszFilter), request, i, "FILTER") )
5074             ;
5075 
5076         /* WFS 2.0 */
5077         else if( msWFSSetParam(&(wfsparams->pszFilterLanguage), request, i, "FILTER_LANGUAGE") )
5078             ;
5079 
5080         else if( msWFSSetParam(&(wfsparams->pszOutputFormat), request, i, "OUTPUTFORMAT") )
5081             ;
5082 
5083         else if( msWFSSetParam(&(wfsparams->pszFeatureId), request, i, "FEATUREID") )
5084             ;
5085 
5086         /* WFS 2.0 */
5087         else if( msWFSSetParam(&(wfsparams->pszFeatureId), request, i, "RESOURCEID") )
5088             ;
5089 
5090         else if( msWFSSetParam(&(wfsparams->pszPropertyName), request, i, "PROPERTYNAME") )
5091             ;
5092 
5093         else if( msWFSSetParam(&(wfsparams->pszAcceptVersions), request, i, "ACCEPTVERSIONS") )
5094             ;
5095 
5096         else if( msWFSSetParam(&(wfsparams->pszSections), request, i, "SECTIONS") )
5097             ;
5098 
5099         else if( msWFSSetParam(&(wfsparams->pszSortBy), request, i, "SORTBY") )
5100             ;
5101 
5102         else if( msWFSSetParam(&(wfsparams->pszLanguage), request, i, "LANGUAGE") )
5103             ;
5104 
5105         else if( msWFSSetParam(&(wfsparams->pszValueReference), request, i, "VALUEREFERENCE") )
5106             ;
5107 
5108         else if( msWFSSetParam(&(wfsparams->pszStoredQueryId), request, i, "STOREDQUERY_ID") )
5109             ;
5110       }
5111     }
5112     /* version is optional is the GetCapabilities. If not */
5113     /* provided, set it. Default it to latest implemented version */
5114     /* or to the specified one in wfs_getcapabilities_version */
5115     if (wfsparams->pszVersion == NULL &&
5116         wfsparams->pszRequest &&
5117         strcasecmp(wfsparams->pszRequest, "GetCapabilities") == 0) {
5118       wfsparams->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
5119     }
5120   }
5121 
5122   /* -------------------------------------------------------------------- */
5123   /*      Parse the post request. It is assumed to be an XML document.    */
5124   /* -------------------------------------------------------------------- */
5125   if (request->postrequest != NULL) {
5126 
5127     CPLXMLNode *psRoot;
5128     CPLXMLNode *psGetFeature = NULL;
5129     CPLXMLNode *psGetCapabilities = NULL;
5130     CPLXMLNode *psDescribeFeature = NULL;
5131     CPLXMLNode *psGetPropertyValue = NULL;
5132     CPLXMLNode *psListStoredQueries = NULL;
5133     CPLXMLNode *psDescribeStoredQueries = NULL;
5134     CPLXMLNode *psOperation = NULL;
5135     const char *pszValue;
5136     char *pszSchemaLocation = NULL;
5137 
5138     psRoot = CPLParseXMLString(request->postrequest);
5139     if(map->debug == MS_DEBUGLEVEL_VVV) {
5140       msDebug("msWFSParseRequest(): WFS post request: %s\n", request->postrequest);
5141     }
5142     if (psRoot) {
5143       /* need to strip namespaces */
5144       CPLStripXMLNamespace(psRoot, NULL, 1);
5145 
5146       for( psOperation = psRoot;
5147            psOperation != NULL;
5148            psOperation = psOperation->psNext ) {
5149         if(psOperation->eType == CXT_Element) {
5150 
5151           /* keep schemaLocation so as to be able to validate against appropriate */
5152           /* wfs.xsd schema afterwards */
5153           if( pszSchemaLocation == NULL &&
5154               CPLGetXMLValue(psOperation, "schemaLocation", NULL) != NULL )
5155              pszSchemaLocation = msStrdup( CPLGetXMLValue(psOperation, "schemaLocation", NULL) );
5156 
5157           if(strcasecmp(psOperation->pszValue,"GetFeature")==0) {
5158             psGetFeature = psOperation;
5159             break;
5160           } else if(strcasecmp(psOperation->pszValue,"GetCapabilities")==0) {
5161             psGetCapabilities = psOperation;
5162             break;
5163           } else if(strcasecmp(psOperation->pszValue,
5164                                "DescribeFeatureType")==0) {
5165             psDescribeFeature = psOperation;
5166             break;
5167           } else if(strcasecmp(psOperation->pszValue,
5168                                "GetPropertyValue")==0) {
5169             psGetPropertyValue = psOperation;
5170             break;
5171           } else if(strcasecmp(psOperation->pszValue,
5172                                "ListStoredQueries")==0) {
5173             psListStoredQueries = psOperation;
5174             break;
5175           } else if(strcasecmp(psOperation->pszValue,
5176                                "DescribeStoredQueries")==0) {
5177             psDescribeStoredQueries = psOperation;
5178             break;
5179           }
5180           /* these are unsupported requests. Just set the  */
5181           /* request value and return; */
5182           else {
5183             int idx = msWFSGetIndexUnsupportedOperation(psOperation->pszValue);
5184             if( idx >= 0 ) {
5185               wfsparams->pszRequest = msStrdup(wfsUnsupportedOperations[idx]);
5186               break;
5187             }
5188           }
5189         }
5190       }
5191 
5192       if( psOperation != NULL )
5193       {
5194         pszValue = CPLGetXMLValue(psOperation, "version",
5195                                   NULL);
5196         if (pszValue)
5197           wfsparams->pszVersion = msStrdup(pszValue);
5198 
5199         pszValue = CPLGetXMLValue(psOperation, "service",
5200                                   NULL);
5201         if (pszValue)
5202           wfsparams->pszService = msStrdup(pszValue);
5203       }
5204 
5205       /* -------------------------------------------------------------------- */
5206       /*      Parse the GetFeature                                            */
5207       /* -------------------------------------------------------------------- */
5208       if (psGetFeature) {
5209         CPLXMLNode* psIter;
5210 
5211         wfsparams->pszRequest = msStrdup("GetFeature");
5212 
5213         pszValue = CPLGetXMLValue(psGetFeature,  "resultType",
5214                                   NULL);
5215         if (pszValue)
5216           wfsparams->pszResultType = msStrdup(pszValue);
5217 
5218         pszValue = CPLGetXMLValue(psGetFeature,  "maxFeatures",
5219                                   NULL);
5220         if (pszValue)
5221           wfsparams->nMaxFeatures = atoi(pszValue);
5222         else
5223         {
5224             /* WFS 2.0 */
5225             pszValue = CPLGetXMLValue(psGetFeature,  "count",
5226                                   NULL);
5227             if (pszValue)
5228                 wfsparams->nMaxFeatures = atoi(pszValue);
5229         }
5230 
5231         pszValue = CPLGetXMLValue(psGetFeature,  "startIndex",
5232                                   NULL);
5233         if (pszValue)
5234           wfsparams->nStartIndex = atoi(pszValue);
5235 
5236         pszValue = CPLGetXMLValue(psGetFeature, "outputFormat",
5237                                   NULL);
5238         if (pszValue)
5239           wfsparams->pszOutputFormat = msStrdup(pszValue);
5240 
5241           /* -------------------------------------------------------------------- */
5242           /*      Parse typenames and filters. If there are multiple queries,     */
5243           /*      typenames will be build with comma between thme                 */
5244           /*      (typename1,typename2,...) and filters will be build with        */
5245           /*      bracets enclosinf the filters :(filter1)(filter2)...            */
5246           /*      propertynames are stored like (property1,property2)(property1)  */
5247           /* -------------------------------------------------------------------- */
5248         psIter = psGetFeature->psChild;
5249         while( psIter != NULL )
5250         {
5251             if( psIter->eType == CXT_Element &&
5252                 strcmp(psIter->pszValue, "Query") == 0 )
5253             {
5254                 msWFSParseXMLQueryNode(psIter, wfsparams);
5255             }
5256             else if( psIter->eType == CXT_Element &&
5257                 strcmp(psIter->pszValue, "StoredQuery") == 0 )
5258             {
5259                 int status = msWFSParseXMLStoredQueryNode(map, wfsparams, psIter);
5260                 if( status != MS_SUCCESS )
5261                 {
5262                     CPLDestroyXMLNode(psRoot);
5263                     msFree(pszSchemaLocation);
5264                     return status;
5265                 }
5266                 wfsparams->bHasPostStoredQuery = MS_TRUE;
5267             }
5268             psIter = psIter->psNext;
5269         }
5270 
5271         msWFSSimplifyPropertyNameAndFilter(wfsparams);
5272       }/* end of GetFeature */
5273 
5274 
5275       /* -------------------------------------------------------------------- */
5276       /*      Parse the GetPropertyValue                                      */
5277       /* -------------------------------------------------------------------- */
5278       if (psGetPropertyValue) {
5279         CPLXMLNode* psIter;
5280 
5281         wfsparams->pszRequest = msStrdup("GetPropertyValue");
5282 
5283         pszValue = CPLGetXMLValue(psGetPropertyValue,  "valueReference",
5284                                   NULL);
5285         if (pszValue)
5286           wfsparams->pszValueReference = msStrdup(pszValue);
5287 
5288         pszValue = CPLGetXMLValue(psGetPropertyValue,  "resultType",
5289                                   NULL);
5290         if (pszValue)
5291           wfsparams->pszResultType = msStrdup(pszValue);
5292 
5293         pszValue = CPLGetXMLValue(psGetPropertyValue,  "count",
5294                                  NULL);
5295         if (pszValue)
5296           wfsparams->nMaxFeatures = atoi(pszValue);
5297 
5298         pszValue = CPLGetXMLValue(psGetPropertyValue,  "startIndex",
5299                                   NULL);
5300         if (pszValue)
5301           wfsparams->nStartIndex = atoi(pszValue);
5302 
5303         pszValue = CPLGetXMLValue(psGetPropertyValue, "outputFormat",
5304                                   NULL);
5305         if (pszValue)
5306           wfsparams->pszOutputFormat = msStrdup(pszValue);
5307 
5308         psIter = psGetPropertyValue->psChild;
5309         while( psIter != NULL )
5310         {
5311             if( psIter->eType == CXT_Element &&
5312                 strcmp(psIter->pszValue, "Query") == 0 )
5313             {
5314                 msWFSParseXMLQueryNode(psIter, wfsparams);
5315                 /* Just one is allowed for GetPropertyValue */
5316                 break;
5317             }
5318             else if( psIter->eType == CXT_Element &&
5319                 strcmp(psIter->pszValue, "StoredQuery") == 0 )
5320             {
5321                 int status = msWFSParseXMLStoredQueryNode(map, wfsparams, psIter);
5322                 if( status != MS_SUCCESS )
5323                 {
5324                     CPLDestroyXMLNode(psRoot);
5325                     msFree(pszSchemaLocation);
5326                     return status;
5327                 }
5328                 wfsparams->bHasPostStoredQuery = MS_TRUE;
5329                 /* Just one is allowed for GetPropertyValue */
5330                 break;
5331             }
5332             psIter = psIter->psNext;
5333         }
5334 
5335         msWFSSimplifyPropertyNameAndFilter(wfsparams);
5336       }/* end of GetPropertyValue */
5337 
5338       /* -------------------------------------------------------------------- */
5339       /*      Parse GetCapabilities.                                          */
5340       /* -------------------------------------------------------------------- */
5341       if (psGetCapabilities) {
5342         CPLXMLNode* psAcceptVersions;
5343         CPLXMLNode* psSections;
5344 
5345         wfsparams->pszRequest = msStrdup("GetCapabilities");
5346 
5347         pszValue = CPLGetXMLValue(psGetCapabilities,  "updateSequence", NULL);
5348         if (pszValue)
5349             wfsparams->pszUpdateSequence = msStrdup(pszValue);
5350 
5351         /* version is optional for the GetCapabilities. If not */
5352         /* provided, set it. */
5353         if (wfsparams->pszVersion == NULL)
5354           wfsparams->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
5355 
5356         psAcceptVersions = CPLGetXMLNode(psGetCapabilities, "AcceptVersions");
5357         if( psAcceptVersions != NULL )
5358         {
5359             CPLXMLNode* psIter = psAcceptVersions->psChild;
5360             while( psIter != NULL )
5361             {
5362                 if( psIter->eType == CXT_Element &&
5363                     strcmp(psIter->pszValue, "Version") == 0 )
5364                 {
5365                     pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5366                     if (pszValue) {
5367                       msWFSBuildParamList(&(wfsparams->pszAcceptVersions), pszValue, ",");
5368                     }
5369                 }
5370                 psIter = psIter->psNext;
5371             }
5372         }
5373 
5374         psSections = CPLGetXMLNode(psGetCapabilities, "Sections");
5375         if( psSections != NULL )
5376         {
5377             CPLXMLNode* psIter = psSections->psChild;
5378             while( psIter != NULL )
5379             {
5380                 if( psIter->eType == CXT_Element &&
5381                     strcmp(psIter->pszValue, "Section") == 0 )
5382                 {
5383                     pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5384                     if (pszValue) {
5385                       msWFSBuildParamList(&(wfsparams->pszSections), pszValue, ",");
5386                     }
5387                 }
5388                 psIter = psIter->psNext;
5389             }
5390         }
5391       }/* end of GetCapabilites */
5392       /* -------------------------------------------------------------------- */
5393       /*      Parse DescribeFeatureType                                       */
5394       /* -------------------------------------------------------------------- */
5395       if (psDescribeFeature) {
5396         CPLXMLNode* psIter;
5397         wfsparams->pszRequest = msStrdup("DescribeFeatureType");
5398 
5399         pszValue = CPLGetXMLValue(psDescribeFeature,
5400                                   "outputFormat",
5401                                   NULL);
5402         if (pszValue)
5403           wfsparams->pszOutputFormat = msStrdup(pszValue);
5404 
5405         psIter = CPLGetXMLNode(psDescribeFeature, "TypeName");
5406         if (psIter) {
5407           /* free typname and filter. There may have been */
5408           /* values if they were passed in the URL */
5409           if (wfsparams->pszTypeName)
5410             free(wfsparams->pszTypeName);
5411           wfsparams->pszTypeName = NULL;
5412 
5413           while (psIter )
5414           {
5415             if( psIter->eType == CXT_Element &&
5416                 strcasecmp(psIter->pszValue, "TypeName") == 0) {
5417               pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5418               if (pszValue) {
5419                 msWFSBuildParamList(&(wfsparams->pszTypeName), pszValue, ",");
5420               }
5421             }
5422             psIter = psIter->psNext;
5423           }
5424         }
5425 
5426       }/* end of DescibeFeatureType */
5427 
5428       /* -------------------------------------------------------------------- */
5429       /*      Parse the ListStoredQueries                                     */
5430       /* -------------------------------------------------------------------- */
5431       if (psListStoredQueries) {
5432         wfsparams->pszRequest = msStrdup("ListStoredQueries");
5433       }/* end of ListStoredQueries */
5434 
5435       /* -------------------------------------------------------------------- */
5436       /*      Parse the DescribeStoredQueries                                 */
5437       /* -------------------------------------------------------------------- */
5438       if (psDescribeStoredQueries) {
5439         CPLXMLNode* psIter;
5440 
5441         wfsparams->pszRequest = msStrdup("DescribeStoredQueries");
5442 
5443         psIter = CPLGetXMLNode(psDescribeStoredQueries, "StoredQueryId");
5444         while (psIter )
5445         {
5446           if( psIter->eType == CXT_Element &&
5447               strcasecmp(psIter->pszValue, "StoredQueryId") == 0) {
5448             pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5449             if (pszValue) {
5450               msWFSBuildParamList(&(wfsparams->pszStoredQueryId), pszValue, ",");
5451             }
5452           }
5453           psIter = psIter->psNext;
5454         }
5455       }/* end of DescribeStoredQueries */
5456 
5457       CPLDestroyXMLNode(psRoot);
5458     }
5459 
5460 #if defined(USE_LIBXML2)
5461     {
5462         const char *schema_location=NULL, *validate=NULL;
5463 
5464         /*do we validate the xml ?*/
5465         validate = msOWSLookupMetadata(&(map->web.metadata), "FO", "validate_xml");
5466         if (validate && strcasecmp(validate, "true") == 0 &&
5467             (schema_location = msOWSLookupMetadata(&(map->web.metadata), "FO", "schemas_dir")))
5468         {
5469             if ((wfsparams->pszService  && strcmp(wfsparams->pszService, "WFS") == 0) ||
5470                 force_wfs_mode)
5471             {
5472                 char *schema_file =NULL;
5473                 if (pszSchemaLocation != NULL && strstr(pszSchemaLocation, "wfs/1.0") != NULL )
5474                 {
5475                     schema_file = msStringConcatenate(schema_file, schema_location);
5476                     schema_file = msStringConcatenate(schema_file, MS_OWSCOMMON_WFS_10_SCHEMA_LOCATION);
5477                 }
5478                 else if (pszSchemaLocation != NULL && strstr(pszSchemaLocation, "wfs/1.1") != NULL )
5479                 {
5480                     schema_file = msStringConcatenate(schema_file, schema_location);
5481                     schema_file = msStringConcatenate(schema_file, MS_OWSCOMMON_WFS_11_SCHEMA_LOCATION);
5482                 }
5483                 else if (pszSchemaLocation != NULL && strstr(pszSchemaLocation, "wfs/2.0") != NULL )
5484                 {
5485                     schema_file = msStringConcatenate(schema_file, schema_location);
5486                     schema_file = msStringConcatenate(schema_file, MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION);
5487                 }
5488                 if( schema_file != NULL )
5489                 {
5490                     if (msOWSSchemaValidation(schema_file, request->postrequest) != 0) {
5491                         msSetError(MS_WFSERR, "Invalid POST request.  XML is not valid", "msWFSParseRequest()");
5492                         msFree(schema_file);
5493                         return msWFSException(map, "request", "InvalidRequest", NULL);
5494                     }
5495                     msFree(schema_file);
5496                 }
5497             }
5498         }
5499     }
5500 #endif
5501 
5502     msFree(pszSchemaLocation);
5503 
5504   }
5505 
5506 #endif
5507   return MS_SUCCESS;
5508 }
5509 
5510