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&VERSION=");
1637 msIO_printf("%s", encoded_version);
1638 msIO_printf("&REQUEST=");
1639 msIO_printf("%s", paramsObj->pszRequest);
1640 msIO_printf("&TYPENAMES=");
1641 msIO_printf("%s", encoded_typename);
1642 msIO_printf("&OUTPUTFORMAT=");
1643 msIO_printf("%s", encoded_mime_type);
1644 if( paramsObj->pszBbox != NULL )
1645 {
1646 msIO_printf("&BBOX=");
1647 msWFSPrintURLAndXMLEncoded(paramsObj->pszBbox);
1648 }
1649 if( paramsObj->pszSrs != NULL )
1650 {
1651 msIO_printf("&SRSNAME=");
1652 msWFSPrintURLAndXMLEncoded(paramsObj->pszSrs);
1653 }
1654 if( paramsObj->pszFilter != NULL )
1655 {
1656 msIO_printf("&FILTER=");
1657 msWFSPrintURLAndXMLEncoded(paramsObj->pszFilter);
1658 }
1659 if( paramsObj->pszPropertyName != NULL)
1660 {
1661 msIO_printf("&PROPERTYNAME=");
1662 msWFSPrintURLAndXMLEncoded(paramsObj->pszPropertyName);
1663 }
1664 if( paramsObj->pszValueReference != NULL)
1665 {
1666 msIO_printf("&VALUEREFERENCE=");
1667 msWFSPrintURLAndXMLEncoded(paramsObj->pszValueReference);
1668 }
1669 if( paramsObj->pszSortBy != NULL )
1670 {
1671 msIO_printf("&SORTBY=");
1672 msWFSPrintURLAndXMLEncoded(paramsObj->pszSortBy);
1673 }
1674 if( paramsObj->nMaxFeatures >= 0 )
1675 msIO_printf("&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("&");
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&VERSION=%s&REQUEST=DescribeFeatureType&TYPENAME=%s&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("&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("&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&VERSION=%s&REQUEST=DescribeFeatureType&TYPENAME=%s&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&VERSION=%s&REQUEST=DescribeFeatureType&TYPENAME=%s&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