/**********************************************************************
* $Id$
*
* Project: MapServer
* Purpose: WFS server implementation
* Author: Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
*
**********************************************************************
* Copyright (c) 2002, Daniel Morissette, DM Solutions Group Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of this Software or works derived from this Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
****************************************************************************/
#include "mapserver.h"
MS_CVSID("$Id$")
#if defined(USE_WFS_SVR)
/* There is a dependency to GDAL/OGR for the GML driver and MiniXML parser */
#include "cpl_minixml.h"
#include "mapogcfilter.h"
#include "mapowscommon.h"
#include "maptemplate.h"
#ifdef WFS_USE_LIBXML2
#include "maplibxml2.h"
#endif
/*
** msWFSException()
**
** Report current MapServer error in XML exception format.
*/
int msWFSException(mapObj *map, const char *locator, const char *code,
const char *version )
{
const char *encoding;
char *schemalocation = NULL;
/* In WFS, exceptions are always XML.
*/
if( version == NULL )
version = "1.1.0";
if( msOWSParseVersionString(version) >= OWS_1_1_0 )
return msWFSException11( map, locator, code, version );
encoding = msOWSLookupMetadata(&(map->web.metadata), "FO", "encoding");
if (encoding)
msIO_setHeader("Content-type","text/xml; charset=%s", encoding);
else
msIO_setHeader("Content-type","text/xml");
msIO_sendHeaders();
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "encoding", OWS_NOERR,
"\n", "ISO-8859-1");
msIO_printf("\n", schemalocation);
free(schemalocation);
msIO_printf(" \n", code, locator);
/* Optional element currently unused. */
/* msIO_printf(" \n"); */
msWriteErrorXML(stdout);
/* msIO_printf(" \n"); */
msIO_printf(" \n");
msIO_printf("\n");
return MS_FAILURE; /* so we can call 'return msWFSException();' anywhere */
}
/*
** Helper function to build a list of output formats.
**
** Given a layer it will return all formats valid for that layer, otherwise
** all formats permitted on layers in the map are returned. The string
** returned should be freed by the caller.
*/
char *msWFSGetOutputFormatList(mapObj *map, layerObj *layer,
const char *version )
{
int i, got_map_list = 0;
static const int out_list_size = 20000;
char *out_list = (char*) msSmallCalloc(1,out_list_size);
if( strncasecmp(version,"1.0",3) != 0 )
strcpy(out_list,"text/xml; subtype=gml/3.1.1");
else
strcpy(out_list,"GML2");
for( i = 0; i < map->numlayers; i++ )
{
const char *format_list;
layerObj *lp;
int j, n;
char **tokens;
lp = GET_LAYER(map, i);
if( layer != NULL && layer != lp )
continue;
format_list = msOWSLookupMetadata(&(lp->metadata),
"F","getfeature_formatlist");
if( format_list == NULL && !got_map_list )
{
format_list = msOWSLookupMetadata(&(map->web.metadata),
"F","getfeature_formatlist");
got_map_list = 1;
}
if( format_list == NULL )
continue;
n = 0;
tokens = msStringSplit(format_list, ',', &n);
for( j = 0; j < n; j++ )
{
int iformat;
const char *fname, *hit;
outputFormatObj *format_obj;
msStringTrim( tokens[j] );
iformat = msGetOutputFormatIndex(map,tokens[j]);
if( iformat < 0 )
continue;
format_obj = map->outputformatlist[iformat];
fname = format_obj->name;
if( strncasecmp(version,"1.0",3) != 0
&& format_obj->mimetype != NULL )
fname = format_obj->mimetype;
hit = strstr(out_list,fname);
if( hit != NULL
&& (hit[strlen(fname)] == '\0' || hit[strlen(fname)] == ','))
continue;
if( strlen(out_list) + strlen(fname)+3 < out_list_size )
{
strcat( out_list, "," );
strcat( out_list, fname );
}
else
break;
}
msFreeCharArray( tokens, n );
}
return out_list;
}
/*
**
*/
static void msWFSPrintRequestCap(const char *wmtver, const char *request,
const char *script_url,
const char *format_tag,
const char *formats_list)
{
msIO_printf(" <%s>\n", request);
/* We expect to receive a NULL-terminated args list of formats */
if (format_tag != NULL)
{
int i, n;
char **tokens;
n = 0;
tokens = msStringSplit(formats_list, ',', &n);
msIO_printf(" <%s>\n", format_tag);
for( i = 0; i < n; i++ )
{
msIO_printf(" <%s/>\n", tokens[i] );
}
msFreeCharArray( tokens, n );
msIO_printf(" %s>\n", format_tag);
}
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n", script_url);
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n", script_url);
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" %s>\n", request);
}
/* msWFSLocateSRSInList()
**
** Utility function to check if a space separated list contains the one passed in argument.
** The list comes normaly from ows_srs metadata, and is expected to use the simple EPSG notation
** (EPSG:4326 ESPG:42304 ...). The srs comes from the query string and can either
** be of simple EPSG format or using gc:def:crs:EPSG:xxx format
*/
int msWFSLocateSRSInList(const char *pszList, const char *srs)
{
int nTokens,i;
char **tokens = NULL;
int bFound = MS_FALSE;
char epsg_string[100];
const char *code;
if (!pszList || !srs)
return MS_FALSE;
if (strncasecmp(srs, "EPSG:",5) == 0)
code = srs+5;
else if (strncasecmp(srs, "urn:ogc:def:crs:EPSG:",21) == 0)
{
if (srs[21] == ':')
code = srs+21;
else
code = srs+20;
while( *code != ':' && *code != '\0')
code++;
if( *code == ':' )
code++;
}
else if (strncasecmp(srs, "urn:EPSG:geographicCRS:",23) == 0)
code = srs + 23;
else
return MS_FALSE;
snprintf( epsg_string, sizeof(epsg_string), "EPSG:%s", code );
tokens = msStringSplit(pszList, ' ', &nTokens );
if (tokens && nTokens > 0)
{
for (i=0; iprojection), &(map->web.metadata), "FO", MS_TRUE);
if(pszMapSRS && nVersion > OWS_1_0_0)
msLoadProjectionStringEPSG(&(map->projection), pszMapSRS);
if (srs == NULL || nVersion == OWS_1_0_0)
{
for (i=0; inumlayers; i++)
{
lp = GET_LAYER(map, i);
if (lp->status != MS_ON)
continue;
if (pszMapSRS)
pszLayerSRS = pszMapSRS;
else
pszLayerSRS = msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE);
if (pszLayerSRS == NULL)
{
msSetError(MS_WFSERR,
"Server config error: SRS must be set at least at the map or at the layer level.",
"msWFSGetFeature()");
if (pszOutputSRS)
msFree(pszOutputSRS);
return MS_FAILURE;
}
if (pszOutputSRS == NULL)
pszOutputSRS = msStrdup(pszLayerSRS);
else if (strcasecmp(pszLayerSRS,pszOutputSRS) != 0)
{
msSetError(MS_WFSERR,
"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.",
"msWFSGetFeature()");
if (pszOutputSRS)
msFree(pszOutputSRS);
return MS_FAILURE;
}
}
}
else /*srs is given so it should be valid for all layers*/
{
/*get all the srs defined at the map level and check them aginst the srsName passed
as argument*/
pszMapSRS = msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_FALSE);
if (pszMapSRS)
{
if (!msWFSLocateSRSInList(pszMapSRS, srs))
{
msSetError(MS_WFSERR,
"Invalid GetFeature Request:Invalid SRS. Please check the capabilities and reformulate your request.",
"msWFSGetFeature()");
return MS_FAILURE;
}
pszOutputSRS = msStrdup(srs);
}
else
{
for (i=0; inumlayers; i++)
{
lp = GET_LAYER(map, i);
if (lp->status != MS_ON)
continue;
pszLayerSRS = msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_FALSE);
if (!pszLayerSRS)
{
msSetError(MS_WFSERR,
"Server config error: SRS must be set at least at the map or at the layer level.",
"msWFSGetFeature()");
return MS_FAILURE;
}
if (!msWFSLocateSRSInList(pszLayerSRS, srs))
{
msSetError(MS_WFSERR,
"Invalid GetFeature Request:Invalid SRS. Please check the capabilities and reformulate your request.",
"msWFSGetFeature()");
return MS_FAILURE;
}
}
pszOutputSRS = msStrdup(srs);
}
}
if (pszOutputSRS && nVersion >= OWS_1_1_0)
{
projectionObj sProjTmp;
int nTmp=0;
msInitProjection(&sProjTmp);
nTmp = msLoadProjectionStringEPSG(&(sProjTmp), pszOutputSRS);
if (nTmp == 0)
{
msProjectRect(&(map->projection), &(sProjTmp), &map->extent);
msFreeProjection(&(sProjTmp));
}
/*check if the srs passed is valid. Assuming that it is an EPSG:xxx format,
Or urn:ogc:def:crs:EPSG:xxx format. */
if (strncasecmp(pszOutputSRS, "EPSG:", 5) == 0 ||
strncasecmp(pszOutputSRS, "urn:ogc:def:crs:EPSG:",21) == 0)
{
/*we load the projection sting in the map and possibly
set the axis order*/
msFreeProjection(&map->projection);
msLoadProjectionStringEPSG(&(map->projection), pszOutputSRS);
}
else if (strncasecmp(pszOutputSRS, "urn:EPSG:geographicCRS:",23) == 0)
{
char epsg_string[100];
const char *code;
code = pszOutputSRS + 23;
snprintf( epsg_string, sizeof(epsg_string), "EPSG:%s", code );
/*we load the projection sting in the map and possibly
set the axis order*/
/*reproject the map extent from current projection to output projection*/
msFreeProjection(&map->projection);
msLoadProjectionStringEPSG(&(map->projection), epsg_string);
}
}
/* Set map output projection to which output features should be reprojected */
else if (pszOutputSRS && strncasecmp(pszOutputSRS, "EPSG:", 5) == 0)
{
int nTmp =0;
projectionObj sProjTmp;
/*reproject the map extent from current projection to output projection*/
msInitProjection(&sProjTmp);
if (nVersion >= OWS_1_1_0)
nTmp = msLoadProjectionStringEPSG(&(sProjTmp), pszOutputSRS);
else
nTmp = msLoadProjectionString(&(sProjTmp), pszOutputSRS);
if (nTmp == 0)
msProjectRect(&(map->projection), &(sProjTmp), &map->extent);
msFreeProjection(&(sProjTmp));
msFreeProjection(&map->projection);
msInitProjection(&map->projection);
if (nVersion >= OWS_1_1_0)
nTmp = msLoadProjectionStringEPSG(&(map->projection), pszOutputSRS);
else
nTmp = msLoadProjectionString(&(map->projection), pszOutputSRS);
if (nTmp != 0) {
msSetError(MS_WFSERR, "msLoadProjectionString() failed", "msWFSGetFeature()");
return MS_FAILURE;
}
}
if (pszOutputSRS)
msFree(pszOutputSRS);
return MS_SUCCESS;
}
/* msWFSIsLayerSupported()
**
** Returns true (1) is this layer meets the requirements to be served as
** a WFS feature type.
*/
int msWFSIsLayerSupported(layerObj *lp)
{
/* In order to be supported, lp->type must be specified, even for
** layers that are OGR, SDE, SDO, etc connections.
*/
if ((lp->type == MS_LAYER_POINT ||
lp->type == MS_LAYER_LINE ||
lp->type == MS_LAYER_POLYGON ) &&
lp->connectiontype != MS_WMS &&
lp->connectiontype != MS_GRATICULE)
{
return 1; /* true */
}
return 0; /* false */
}
/* msWFSGetGeomElementName()
**
** Return the geometry propery name base on the layer type
*/
const char *msWFSGetGeomElementName(mapObj *map, layerObj *lp)
{
switch(lp->type)
{
case MS_LAYER_POINT:
return "pointProperty";
case MS_LAYER_LINE:
return "lineStringProperty";
case MS_LAYER_POLYGON:
return "polygonProperty";
default:
break;
}
return "???unknown???";
}
/* msWFSGetGeomType()
**
** Return GML name for geometry type in this layer
** This is based on MapServer geometry type and layers with mixed geometries
** may not return the right feature type.
*/
#ifdef notdef /* not currently used */
static const char *msWFSGetGeomType(layerObj *lp)
{
switch(lp->type)
{
case MS_LAYER_POINT:
return "PointPropertyType";
case MS_LAYER_LINE:
return "LineStringPropertyType";
case MS_LAYER_POLYGON:
return "PolygonPropertyType";
default:
break;
}
return "???unknown???";
}
#endif /* def notdef */
/*
** msWFSDumpLayer()
*/
int msWFSDumpLayer(mapObj *map, layerObj *lp)
{
rectObj ext;
const char *pszWfsSrs = NULL;
projectionObj poWfs;
int result = 0;
msIO_printf(" \n");
if (lp->name && strlen(lp->name) > 0 &&
(msIsXMLTagValid(lp->name) == MS_FALSE || isdigit(lp->name[0])))
msIO_fprintf(stdout, "\n",lp->name);
msOWSPrintEncodeParam(stdout, "LAYER.NAME", lp->name, OWS_WARN,
" %s\n", NULL);
msOWSPrintEncodeMetadata(stdout, &(lp->metadata), "FO", "title",
OWS_WARN, " %s\n", lp->name);
msOWSPrintEncodeMetadata(stdout, &(lp->metadata), "FO", "abstract",
OWS_NOERR, " %s\n", NULL);
msOWSPrintEncodeMetadataList(stdout, &(lp->metadata), "FO",
"keywordlist",
" \n",
" \n",
" %s\n", NULL);
/* In WFS, every layer must have exactly one SRS and there is none at */
/* the top level contrary to WMS */
/* */
/* So here is the way we'll deal with SRS: */
/* 1- If a top-level map projection (or wfs_srs metadata) is set then */
/* all layers are advertized in the map's projection and they will */
/* be reprojected on the fly in the GetFeature request. */
/* 2- If there is no top-level map projection (or wfs_srs metadata) then */
/* each layer is advertized in its own projection as defined in the */
/* layer's projection object or wfs_srs metadata. */
/* */
if (msOWSGetEPSGProj(&(map->projection),&(map->web.metadata),"FO",MS_TRUE) != NULL)
{
/* Map has a SRS. Use it for all layers. */
pszWfsSrs = msOWSGetEPSGProj(&(map->projection),&(map->web.metadata), "FO", MS_TRUE);
}
else
{
/* Map has no SRS. Use layer SRS or produce a warning. */
pszWfsSrs = msOWSGetEPSGProj(&(lp->projection),&(lp->metadata), "FO", MS_TRUE);
}
msOWSPrintEncodeParam(stdout, "(at least one of) MAP.PROJECTION, LAYER.PROJECTION or wfs_srs metadata",
pszWfsSrs, OWS_WARN, " %s\n", NULL);
/* If layer has no proj set then use map->proj for bounding box. */
if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS)
{
msInitProjection(&poWfs);
if (pszWfsSrs != NULL)
result = msLoadProjectionString(&(poWfs), pszWfsSrs);
if(lp->projection.numargs > 0)
{
msOWSPrintLatLonBoundingBox(stdout, " ", &(ext),
&(lp->projection), &(poWfs), OWS_WFS);
}
else
{
msOWSPrintLatLonBoundingBox(stdout, " ", &(ext),
&(map->projection), &(poWfs), OWS_WFS);
}
msFreeProjection(&poWfs);
}
else
{
msIO_printf("\n");
}
msOWSPrintURLType(stdout, &(lp->metadata), "FO", "metadataurl",
OWS_NOERR, NULL, "MetadataURL", " type=\"%s\"",
NULL, NULL, " format=\"%s\"", "%s",
MS_TRUE, MS_FALSE, MS_FALSE, MS_TRUE, MS_TRUE,
NULL, NULL, NULL, NULL, NULL, " ");
if (msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid") == NULL)
{
msIO_fprintf(stdout, "\n");
}
msIO_printf(" \n");
return MS_SUCCESS;
}
/*
** msWFSGetCapabilities()
*/
int msWFSGetCapabilities(mapObj *map, wfsParamsObj *wfsparams, cgiRequestObj *req, owsRequestObj *ows_request)
{
char *script_url=NULL, *script_url_encoded;
const char *updatesequence=NULL;
const char *wmtver=NULL;
const char *encoding;
char *formats_list;
char tmpString[OWS_VERSION_MAXLEN];
int wfsSupportedVersions[] = {OWS_1_1_0, OWS_1_0_0};
int wfsNumSupportedVersions = 2;
int i=0, tmpInt=0;
/* acceptversions: do OWS Common style of version negotiation */
if (wfsparams->pszAcceptVersions && strlen(wfsparams->pszAcceptVersions) > 0)
{
char **tokens;
int i, j;
tokens = msStringSplit(wfsparams->pszAcceptVersions, ',', &j);
for (i=0; ipszAcceptVersions);
return msWFSException(map, "acceptversions", "VersionNegotiationFailed",wmtver);
}
}
else
/* negotiate version */
tmpInt = msOWSNegotiateVersion(msOWSParseVersionString(wfsparams->pszVersion), wfsSupportedVersions,
wfsNumSupportedVersions);
/* set result as string and carry on */
if (wfsparams->pszVersion)
msFree(wfsparams->pszVersion);
wfsparams->pszVersion = msStrdup(msOWSGetVersionString(tmpInt, tmpString));
if( wfsparams->pszVersion == NULL || strncmp(wfsparams->pszVersion,"1.1",3) == 0 )
return msWFSGetCapabilities11( map, wfsparams, req, ows_request);
/* Decide which version we're going to return... only 1.0.0 for now */
wmtver = "1.0.0";
/* We need this server's onlineresource. */
if ((script_url=msOWSGetOnlineResource(map, "FO", "onlineresource", req)) == NULL ||
(script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL)
{
msSetError(MS_WFSERR, "Server URL not found", "msWFSGetCapabilities()");
return msWFSException(map, "mapserv", "NoApplicableCode", wmtver);
}
free(script_url);
script_url = NULL;
updatesequence = msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence");
if (!updatesequence)
updatesequence = msStrdup("0");
if (wfsparams->pszUpdateSequence != NULL) {
i = msOWSNegotiateUpdateSequence(wfsparams->pszUpdateSequence, updatesequence);
if (i == 0) { /* current */
msSetError(MS_WFSERR, "UPDATESEQUENCE parameter (%s) is equal to server (%s)", "msWFSGetCapabilities()", wfsparams->pszUpdateSequence, updatesequence);
free(script_url_encoded);
return msWFSException(map, "updatesequence", "CurrentUpdateSequence", wmtver);
}
if (i > 0) { /* invalid */
msSetError(MS_WFSERR, "UPDATESEQUENCE parameter (%s) is higher than server (%s)", "msWFSGetCapabilities()", wfsparams->pszUpdateSequence, updatesequence);
free(script_url_encoded);
return msWFSException(map, "updatesequence", "InvalidUpdateSequence", wmtver);
}
}
encoding = msOWSLookupMetadata(&(map->web.metadata), "FO", "encoding");
if (encoding)
msIO_setHeader("Content-type","text/xml; charset=%s", encoding);
else
msIO_setHeader("Content-type","text/xml");
msIO_sendHeaders();
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "encoding", OWS_NOERR,
"\n",
"ISO-8859-1");
msIO_printf("\n",
wmtver, updatesequence,
msOWSGetSchemasLocation(map), wmtver);
/* Report MapServer Version Information */
msIO_printf("\n\n\n", msGetVersion());
/*
** SERVICE definition
*/
msIO_printf("\n");
msIO_printf(" MapServer WFS\n");
/* the majority of this section is dependent on appropriately named metadata in the WEB object */
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "title",
OWS_WARN, " %s\n", map->name);
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "abstract",
OWS_NOERR, " %s\n", NULL);
msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "FO",
"keywordlist",
" \n", " \n",
" %s\n", NULL);
/* Service/onlineresource */
/* Defaults to same as request onlineresource if wfs_service_onlineresource */
/* is not set. */
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata),
"FO", "service_onlineresource", OWS_NOERR,
" %s\n",
script_url_encoded);
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "fees",
OWS_NOERR, " %s\n", NULL);
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO",
"accessconstraints", OWS_NOERR,
" %s\n",
NULL);
msIO_printf("\n\n");
/*
** CAPABILITY definitions: list of supported requests
*/
msIO_printf("\n");
msIO_printf(" \n");
msWFSPrintRequestCap(wmtver, "GetCapabilities", script_url_encoded,
NULL, NULL);
/* msWFSPrintRequestCap(wmtver, "DescribeFeatureType", script_url_encoded, "SchemaDescriptionLanguage", "XMLSCHEMA", "SFE_XMLSCHEMA", NULL); */
/* msWFSPrintRequestCap(wmtver, "GetFeature", script_url_encoded, "ResultFormat", "GML2", "GML3", NULL); */
/* don't advertise the GML3 or GML for SFE support */
if (msOWSRequestIsEnabled(map, NULL, "F", "DescribeFeatureType", MS_TRUE))
msWFSPrintRequestCap(wmtver, "DescribeFeatureType", script_url_encoded, "SchemaDescriptionLanguage", "XMLSCHEMA" );
if (msOWSRequestIsEnabled(map, NULL, "F", "GetFeature", MS_TRUE))
{
formats_list = msWFSGetOutputFormatList( map, NULL, wfsparams->pszVersion );
msWFSPrintRequestCap(wmtver, "GetFeature", script_url_encoded, "ResultFormat", formats_list );
msFree( formats_list );
}
msIO_printf(" \n");
msIO_printf("\n\n");
/*
** FeatureTypeList: layers
*/
msIO_printf("\n");
/* Operations supported... set default at top-level, and more operations */
/* can be added inside each layer... for MapServer only query is supported */
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
for(i=0; inumlayers; i++)
{
layerObj *lp;
lp = GET_LAYER(map, i);
if (lp->status == MS_DELETE)
continue;
if (!msIntegerInArray(lp->index, ows_request->enabled_layers, ows_request->numlayers))
continue;
if (msWFSIsLayerSupported(lp))
{
msWFSDumpLayer(map, lp);
}
}
msIO_printf("\n\n");
/*
** OGC Filter Capabilities ... for now we support only BBOX
*/
msIO_printf("\n");
msIO_printf(" \n");
msIO_printf(" \n");
#ifdef USE_GEOS
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
#endif
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf("\n\n");
/*
** Done!
*/
msIO_printf("\n");
free(script_url_encoded);
return MS_SUCCESS;
}
/*
** Helper functions for producing XML schema.
*/
static const char *msWFSGetGeometryType(char *type, int outputformat)
{
if(!type) return "???undefined???";
if(strcasecmp(type, "point") == 0) {
switch(outputformat) {
case OWS_GML2:
case OWS_GML3:
return "PointPropertyType";
}
} else if(strcasecmp(type, "multipoint") == 0) {
switch(outputformat) {
case OWS_GML2:
case OWS_GML3:
return "MultiPointPropertyType";
}
} else if(strcasecmp(type, "line") == 0) {
switch(outputformat) {
case OWS_GML2:
return "LineStringPropertyType";
case OWS_GML3:
return "CurvePropertyType";
}
} else if(strcasecmp(type, "multiline") == 0) {
switch(outputformat) {
case OWS_GML2:
return "MultiLineStringPropertyType";
case OWS_GML3:
return "MultiCurvePropertyType";
}
} else if(strcasecmp(type, "polygon") == 0) {
switch(outputformat) {
case OWS_GML2:
return "PolygonPropertyType";
case OWS_GML3:
return "SurfacePropertyType";
}
} else if(strcasecmp(type, "multipolygon") == 0) {
switch(outputformat) {
case OWS_GML2:
return "MultiPolygonPropertyType";
case OWS_GML3:
return "MultiSurfacePropertyType";
}
}
return "???unkown???";
}
static void msWFSWriteGeometryElement(FILE *stream, gmlGeometryListObj *geometryList, int outputformat, const char *tab)
{
int i;
gmlGeometryObj *geometry=NULL;
if(!stream || !tab) return;
if(geometryList && geometryList->numgeometries == 1 && strcasecmp(geometryList->geometries[0].name, "none") == 0) return;
if(!geometryList || geometryList->numgeometries == 0) { /* write a default container */
msIO_fprintf(stream, "%s\n", tab, OWS_GML_DEFAULT_GEOMETRY_NAME);
return;
} else {
if(geometryList->numgeometries == 1) {
geometry = &(geometryList->geometries[0]);
msIO_fprintf(stream, "%sname, msWFSGetGeometryType(geometry->type, outputformat), geometry->occurmin);
if(geometry->occurmax == OWS_GML_OCCUR_UNBOUNDED)
msIO_fprintf(stream, " maxOccurs=\"unbounded\"/>\n");
else
msIO_fprintf(stream, " maxOccurs=\"%d\"/>\n", geometry->occurmax);
} else {
msIO_fprintf(stream, "%s\n", tab);
for(i=0; inumgeometries; i++) {
geometry = &(geometryList->geometries[i]);
msIO_fprintf(stream, " %sname, msWFSGetGeometryType(geometry->type, outputformat), geometry->occurmin);
if(geometry->occurmax == OWS_GML_OCCUR_UNBOUNDED)
msIO_fprintf(stream, " maxOccurs=\"unbounded\"/>\n");
else
msIO_fprintf(stream, " maxOccurs=\"%d\"/>\n", geometry->occurmax);
}
msIO_fprintf(stream, "%s\n", tab);
}
}
return;
}
static void msWFSWriteItemElement(FILE *stream, gmlItemObj *item, const char *tab)
{
char *element_name;
char *element_type = "string";
if(!stream || !item || !tab) return;
if(!item->visible) return; /* not exposing this attribute */
if(item->template) return; /* can't adequately deal with templated items yet */
if(item->alias) /* TODO: what about name spaces embedded in the alias? */
element_name = item->alias;
else
element_name = item->name;
if(item->type)
element_type = item->type;
msIO_fprintf(stream, "%s\n", tab, element_name, element_type);
return;
}
static void msWFSWriteConstantElement(FILE *stream, gmlConstantObj *constant, const char *tab)
{
char *element_type = "string";
if(!stream || !constant || !tab) return;
if(constant->type)
element_type = constant->type;
msIO_fprintf(stream, "%s\n", tab, constant->name, element_type);
return;
}
static void msWFSWriteGroupElement(FILE *stream, gmlGroupObj *group, const char *tab, const char *namespace)
{
if(group->type)
msIO_fprintf(stream, "%s\n", tab, group->name, namespace, group->type);
else
msIO_fprintf(stream, "%s\n", tab, group->name, namespace, group->name);
return;
}
static void msWFSWriteGroupElementType(FILE *stream, gmlGroupObj *group, gmlItemListObj *itemList, gmlConstantListObj *constantList, const char *tab)
{
int i, j;
char *element_tab;
gmlItemObj *item=NULL;
gmlConstantObj *constant=NULL;
/* setup the element tab */
element_tab = (char *) malloc(sizeof(char)*strlen(tab)+5);
MS_CHECK_ALLOC_NO_RET(element_tab, sizeof(char)*strlen(tab)+5);
sprintf(element_tab, "%s ", tab);
if(group->type)
msIO_fprintf(stream, "%s\n", tab, group->type);
else
msIO_fprintf(stream, "%s\n", tab, group->name);
msIO_fprintf(stream, "%s \n", tab);
/* now the items/constants (e.g. elements) in the group */
for(i=0; inumitems; i++) {
for(j=0; jnumconstants; j++) { /* find the right gmlConstantObj */
constant = &(constantList->constants[j]);
if(strcasecmp(constant->name, group->items[i]) == 0) {
msWFSWriteConstantElement(stream, constant, element_tab);
break;
}
}
if(j != constantList->numconstants) continue; /* found this item */
for(j=0; jnumitems; j++) { /* find the right gmlItemObj */
item = &(itemList->items[j]);
if(strcasecmp(item->name, group->items[i]) == 0) {
msWFSWriteItemElement(stream, item, element_tab);
break;
}
}
}
msIO_fprintf(stream, "%s \n", tab);
msIO_fprintf(stream, "%s\n", tab);
return;
}
/*
** msWFSDescribeFeatureType()
*/
int msWFSDescribeFeatureType(mapObj *map, wfsParamsObj *paramsObj, owsRequestObj *ows_request)
{
int i, numlayers=0;
char **layers = NULL;
char **tokens;
int n=0;
const char *value;
const char *user_namespace_prefix = "ms";
const char *user_namespace_uri = "http://mapserver.gis.umn.edu/mapserver";
char *user_namespace_uri_encoded = NULL;
const char *collection_name = OWS_WFS_FEATURE_COLLECTION_NAME;
char *encoded_name = NULL, *encoded;
int outputformat = OWS_DEFAULT_SCHEMA; /* default output is GML 2.1 compliant schema*/
gmlNamespaceListObj *namespaceList=NULL; /* for external application schema support */
char *mimetype = NULL;
if(paramsObj->pszTypeName && numlayers == 0) {
/* Parse comma-delimited list of type names (layers) */
/* */
/* __TODO__ Need to handle type grouping, e.g. "(l1,l2),l3,l4" */
/* */
layers = msStringSplit(paramsObj->pszTypeName, ',', &numlayers);
if (numlayers > 0) {
/* strip namespace if there is one :ex TYPENAME=cdf:Other */
tokens = msStringSplit(layers[0], ':', &n);
if (tokens && n==2 && msGetLayerIndex(map, layers[0]) < 0) {
msFreeCharArray(tokens, n);
tokens = NULL;
for (i=0; ipszOutputFormat) {
if(strcasecmp(paramsObj->pszOutputFormat, "XMLSCHEMA") == 0 ||
strstr(paramsObj->pszOutputFormat, "gml/2")!= NULL)
{
mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/2.1.2");
outputformat = OWS_DEFAULT_SCHEMA;
}
else if(strcasecmp(paramsObj->pszOutputFormat, "SFE_XMLSCHEMA") == 0 ||
strstr(paramsObj->pszOutputFormat, "gml/3")!= NULL)
{
mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/3.1.1");
outputformat = OWS_SFE_SCHEMA;
}
else {
msSetError(MS_WFSERR, "Unsupported DescribeFeatureType outputFormat (%s).", "msWFSDescribeFeatureType()", paramsObj->pszOutputFormat);
return msWFSException(map, "outputformat", "InvalidParameterValue", paramsObj->pszVersion);
}
}
/*set the output format to gml3 for wfs1.1*/
if(mimetype == NULL)
{
if (paramsObj->pszVersion == NULL || strncmp(paramsObj->pszVersion,"1.1",3) == 0 )
{
mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/3.1.1");
outputformat = OWS_SFE_SCHEMA;
}
else
mimetype = msEncodeHTMLEntities("text/xml");
}
/* Validate layers */
if (numlayers > 0) {
for (i=0; iindex, ows_request->enabled_layers, ows_request->numlayers)) ) {
msSetError(MS_WFSERR, "Invalid typename (%s).", "msWFSDescribeFeatureType()", layers[i]);/* paramsObj->pszTypeName); */
return msWFSException(map, "typename", "InvalidParameterValue", paramsObj->pszVersion);
}
}
}
/*
** retrieve any necessary external namespace/schema configuration information
*/
namespaceList = msGMLGetNamespaces(&(map->web), "G");
if (namespaceList == NULL)
{
msSetError(MS_MISCERR, "Unable to populate namespace list", "msWFSDescribeFeatureType()");
return MS_FAILURE;
}
/*
** DescribeFeatureType response
*/
value = msOWSLookupMetadata(&(map->web.metadata), "FO", "encoding");
if (value)
msIO_setHeader("Content-type","%s; charset=%s",mimetype, value);
else
msIO_setHeader("Content-type",mimetype);
msIO_sendHeaders();
if (mimetype)
msFree(mimetype);
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "encoding", OWS_NOERR,
"\n",
"ISO-8859-1");
value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
if(value) user_namespace_uri = value;
user_namespace_uri_encoded = msEncodeHTMLEntities(user_namespace_uri);
value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
if(value) user_namespace_prefix = value;
if(user_namespace_prefix != NULL && msIsXMLTagValid(user_namespace_prefix) == MS_FALSE)
msIO_printf("\n", user_namespace_prefix);
msIO_printf("numnamespaces; i++) {
if(namespaceList->namespaces[i].uri) {
char *uri_encoded=NULL;
uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
msIO_printf(" xmlns:%s=\"%s\" \n", namespaceList->namespaces[i].prefix, uri_encoded);
msFree(uri_encoded);
}
}
msIO_printf(" elementFormDefault=\"qualified\" version=\"0.1\" >\n");
encoded = msEncodeHTMLEntities( msOWSGetSchemasLocation(map) );
if(outputformat == OWS_SFE_SCHEMA) /* reference GML 3.1.1 schema */
msIO_printf("\n \n", encoded);
else /* default GML 2.1.x schema */
msIO_printf("\n \n", encoded);
msFree(encoded);
/* any additional namespace includes */
for(i=0; inumnamespaces; i++) {
if(namespaceList->namespaces[i].uri && namespaceList->namespaces[i].schemalocation) {
char *schema_location_encoded=NULL, *uri_encoded=NULL;
uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
schema_location_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].schemalocation);
msIO_printf("\n \n", uri_encoded, schema_location_encoded);
msFree(uri_encoded);
msFree(schema_location_encoded);
}
}
/* output definition for the default feature container, can't use wfs:FeatureCollection with GML3:
kept here so that the behaviour with whs1.0 and gml3 output is preserved.
We can use the wfs:FeatureCollection for wfs1.1*/
if(outputformat == OWS_SFE_SCHEMA && strncmp(paramsObj->pszVersion,"1.1",3) != 0) {
value = msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection");
if(value) collection_name = value;
msIO_printf(" \n", collection_name, user_namespace_prefix, collection_name);
msIO_printf(" \n", collection_name);
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
}
/*
** loop through layers
*/
for(i=0; inumlayers; i++) {
layerObj *lp;
int j, bFound = 0;
lp = GET_LAYER(map, i);
for (j=0; jname && strcasecmp(lp->name, layers[j]) == 0)
bFound = 1;
}
if ((numlayers == 0 || bFound) && msWFSIsLayerSupported(lp) &&
(msIntegerInArray(lp->index, ows_request->enabled_layers, ows_request->numlayers)) ) {
/*
** OK, describe this layer IF you can open it and retrieve items
*/
if (msLayerOpen(lp) == MS_SUCCESS) {
if (msLayerGetItems(lp) == MS_SUCCESS) {
int k;
gmlGroupListObj *groupList=NULL;
gmlItemListObj *itemList=NULL;
gmlConstantListObj *constantList=NULL;
gmlGeometryListObj *geometryList=NULL;
gmlItemObj *item=NULL;
gmlConstantObj *constant=NULL;
const char *layer_namespace_prefix;
char *encoded_type=NULL;
itemList = msGMLGetItems(lp, "G"); /* GML-related metadata */
constantList = msGMLGetConstants(lp, "G");
groupList = msGMLGetGroups(lp, "G");
geometryList = msGMLGetGeometries(lp, "GFO");
if (itemList == NULL || constantList == NULL || groupList == NULL || geometryList == NULL)
{
msSetError(MS_MISCERR, "Unable to populate item and group metadata structures", "msWFSDescribeFeatureType()");
return MS_FAILURE;
}
value = msOWSLookupMetadata(&(lp->metadata), "OFG", "namespace_prefix");
if(value)
layer_namespace_prefix = value;
else
layer_namespace_prefix = user_namespace_prefix;
/* value = msOWSLookupMetadata(&(lp->metadata), "OFG", "layername"); */
encoded_name = msEncodeHTMLEntities( lp->name );
value = msOWSLookupMetadata(&(lp->metadata), "OFG", "layer_type");
if(value) {
encoded_type = msEncodeHTMLEntities(value);
msIO_printf("\n"
" \n\n",
encoded_name, layer_namespace_prefix, encoded_type);
msFree(encoded_type);
} else
msIO_printf("\n"
" \n\n",
encoded_name, layer_namespace_prefix, encoded_name);
if(strcmp(layer_namespace_prefix, user_namespace_prefix) != 0)
continue; /* the rest is defined in an external schema */
msIO_printf(" \n", encoded_name);
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
/* write the geometry schema element(s) */
msWFSWriteGeometryElement(stdout, geometryList, outputformat, " ");
/* write the constant-based schema elements */
for(k=0; knumconstants; k++) {
constant = &(constantList->constants[k]);
if(msItemInGroups(constant->name, groupList) == MS_FALSE)
msWFSWriteConstantElement(stdout, constant, " ");
}
/* write the item-based schema elements */
for(k=0; knumitems; k++) {
item = &(itemList->items[k]);
if(msItemInGroups(item->name, groupList) == MS_FALSE)
msWFSWriteItemElement(stdout, item, " ");
}
for(k=0; knumgroups; k++)
msWFSWriteGroupElement(stdout, &(groupList->groups[k]), " ", user_namespace_prefix);
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
msIO_printf(" \n");
/* any group types */
for(k=0; knumgroups; k++)
msWFSWriteGroupElementType(stdout, &(groupList->groups[k]), itemList, constantList, " ");
msGMLFreeItems(itemList);
msGMLFreeConstants(constantList);
msGMLFreeGroups(groupList);
msGMLFreeGeometries(geometryList);
}
msLayerClose(lp);
} else {
msIO_printf("\n\n\n\n", encoded_name);
}
}
}
/*
** Done!
*/
msIO_printf("\n\n");
msFree(encoded_name);
msFree(user_namespace_uri_encoded);
if(layers)
msFreeCharArray(layers, numlayers);
msGMLFreeNamespaces(namespaceList);
return MS_SUCCESS;
}
/*
** msWFSGetFeature_GMLPreamble()
**
** Generate the GML preamble up to the first feature for the builtin
** WFS GML support.
*/
typedef struct {
const char *user_namespace_prefix;
const char *user_namespace_uri;
char *user_namespace_uri_encoded;
const char *collection_name;
const char *typename;
char *script_url, *script_url_encoded;
const char *output_schema_format;
} WFSGMLInfo;
static int msWFSGetFeature_GMLPreamble( mapObj *map,
cgiRequestObj *req,
WFSGMLInfo *gmlinfo,
wfsParamsObj *paramsObj,
int outputformat,
int iResultTypeHits,
int iNumberOfFeatures )
{
const char *value;
int i;
char *encoded, *encoded_typename, *encoded_schema;
gmlNamespaceListObj *namespaceList=NULL; /* for external application schema support */
namespaceList = msGMLGetNamespaces(&(map->web), "G");
if (namespaceList == NULL)
{
msSetError(MS_MISCERR, "Unable to populate namespace list", "msWFSGetFeature_GMLPreamble()");
return MS_FAILURE;
}
/*
** Establish script_url.
*/
if ((gmlinfo->script_url=msOWSGetOnlineResource(map,"FO","onlineresource",req)) ==NULL ||
(gmlinfo->script_url_encoded = msEncodeHTMLEntities(gmlinfo->script_url)) == NULL) {
msSetError(MS_WFSERR, "Server URL not found", "msWFSGetFeature()");
return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion);
}
/*
** Write encoding.
*/
msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO",
"encoding", OWS_NOERR,
"\n",
"ISO-8859-1");
value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
if(value) gmlinfo->user_namespace_uri = value;
gmlinfo->user_namespace_uri_encoded =
msEncodeHTMLEntities(gmlinfo->user_namespace_uri);
value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
if(value) gmlinfo->user_namespace_prefix = value;
if(gmlinfo->user_namespace_prefix != NULL && msIsXMLTagValid(gmlinfo->user_namespace_prefix) == MS_FALSE)
msIO_printf("\n", gmlinfo->user_namespace_prefix);
value = msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection");
if(value) gmlinfo->collection_name = value;
encoded = msEncodeHTMLEntities( paramsObj->pszVersion );
encoded_typename = msEncodeHTMLEntities( gmlinfo->typename );
encoded_schema = msEncodeHTMLEntities( msOWSGetSchemasLocation(map) );
/*
** GML 2.x
*/
if(outputformat == OWS_GML2) { /* use a wfs:FeatureCollection */
msIO_printf("user_namespace_prefix,
gmlinfo->user_namespace_uri_encoded);
/* any additional namespaces */
for(i=0; inumnamespaces; i++) {
if(namespaceList->namespaces[i].uri) {
char *uri_encoded=NULL;
uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
msIO_printf(" xmlns:%s=\"%s\" \n", namespaceList->namespaces[i].prefix, uri_encoded);
msFree(uri_encoded);
}
}
msIO_printf(" xsi:schemaLocation=\"http://www.opengis.net/wfs %s/wfs/%s/WFS-basic.xsd \n"
" %s %sSERVICE=WFS&VERSION=%s&REQUEST=DescribeFeatureType&TYPENAME=%s&OUTPUTFORMAT=%s\">\n",
encoded_schema, encoded,
gmlinfo->user_namespace_uri_encoded,
gmlinfo->script_url_encoded, encoded,
encoded_typename, gmlinfo->output_schema_format);
}
/*
** GML 3
*/
else
{
if(paramsObj->pszVersion && strncmp(paramsObj->pszVersion,"1.1",3) == 0 )
{
msIO_printf("user_namespace_prefix,
gmlinfo->user_namespace_uri_encoded);
}
else
{
msIO_printf("<%s:%s\n"
" version=\"1.0.0\"\n"
" xmlns:%s=\"%s\"\n"
" xmlns:gml=\"http://www.opengis.net/gml\"\n"
" xmlns:ogc=\"http://www.opengis.net/ogc\"\n"
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n",
gmlinfo->user_namespace_prefix,
gmlinfo->collection_name,
gmlinfo->user_namespace_prefix,
gmlinfo->user_namespace_uri_encoded);
}
/* any additional namespaces */
for(i=0; inumnamespaces; i++) {
if(namespaceList->namespaces[i].uri) {
char *uri_encoded=NULL;
uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
msIO_printf(" xmlns:%s=\"%s\" \n", namespaceList->namespaces[i].prefix, uri_encoded);
msFree(uri_encoded);
}
}
if(paramsObj->pszVersion && strncmp(paramsObj->pszVersion,"1.1",3) == 0)
{
if (iResultTypeHits == 1)
{
char timestring[100];
struct tm *now;
time_t tim=time(NULL);
now=localtime(&tim);
snprintf(timestring, sizeof(timestring), "%d-%02d-%02dT%02d:%02d:%02d",
now->tm_year+1900, now->tm_mon+1, now->tm_mday,
now->tm_hour, now->tm_min, now->tm_sec);
msIO_printf(" xsi:schemaLocation=\"%s %sSERVICE=WFS&VERSION=%s&REQUEST=DescribeFeatureType&TYPENAME=%s&OUTPUTFORMAT=%s http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\" timeStamp=\"%s\" numberOfFeatures=\"%d\">\n",
gmlinfo->user_namespace_uri_encoded,
gmlinfo->script_url_encoded, encoded,
encoded_typename,
gmlinfo->output_schema_format,
timestring, iNumberOfFeatures);
}
else
msIO_printf(" xsi:schemaLocation=\"%s %sSERVICE=WFS&VERSION=%s&REQUEST=DescribeFeatureType&TYPENAME=%s&OUTPUTFORMAT=%s http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\">\n",
gmlinfo->user_namespace_uri_encoded,
gmlinfo->script_url_encoded, encoded,
encoded_typename,
gmlinfo->output_schema_format);
}
else
msIO_printf(" xsi:schemaLocation=\"%s %sSERVICE=WFS&VERSION=%s&REQUEST=DescribeFeatureType&TYPENAME=%s&OUTPUTFORMAT=%s\">\n",
gmlinfo->user_namespace_uri_encoded,
gmlinfo->script_url_encoded, encoded,
encoded_typename, gmlinfo->output_schema_format);
}
msFree(encoded);
msFree(encoded_schema);
msFree(encoded_typename);
msGMLFreeNamespaces(namespaceList);
return MS_SUCCESS;
}
/*
** msWFSGetFeature_GMLPostfix()
**
** Generate the GML file tail closing the collection and cleanup a bit.
*/
static int msWFSGetFeature_GMLPostfix( mapObj *map,
cgiRequestObj *req,
WFSGMLInfo *gmlinfo,
wfsParamsObj *paramsObj,
int outputformat,
int maxfeatures,
int iResultTypeHits,
int iNumberOfFeatures )
{
if (((iNumberOfFeatures==0) || (maxfeatures == 0)) && iResultTypeHits == 0) {
msIO_printf(" \n");
if(outputformat == OWS_GML3)
msIO_printf(" missing\n");
else
msIO_printf(" missing\n");
msIO_printf(" \n");
}
if(outputformat == OWS_GML2)
msIO_printf("\n\n");
else
{
if(paramsObj->pszVersion && strncmp(paramsObj->pszVersion,"1.1",3) == 0)
msIO_printf("\n\n", gmlinfo->user_namespace_prefix, gmlinfo->collection_name);
else
msIO_printf("%s:%s>\n\n", gmlinfo->user_namespace_prefix, gmlinfo->collection_name);
}
free(gmlinfo->script_url);
free(gmlinfo->script_url_encoded);
msFree(gmlinfo->user_namespace_uri_encoded);
return MS_SUCCESS;
}
/*
** msWFSGetFeature()
*/
int msWFSGetFeature(mapObj *map, wfsParamsObj *paramsObj, cgiRequestObj *req, owsRequestObj *ows_request)
/* const char *wmtver, char **names, char **values, int numentries) */
{
int i, j, status;
int maxfeatures=-1,startindex=-1;
rectObj bbox;
char **layers = NULL;
int numlayers = 0;
char *pszFilter = NULL;
int bFilterSet = 0;
int bBBOXSet = 0;
char *sBBoxSrs = NULL;
int bFeatureIdSet = 0;
const char *value;
const char *tmpmaxfeatures = NULL;
WFSGMLInfo gmlinfo;
const char *output_mime_type = "text/xml; subtype=gml/3.1.1";
int outputformat = OWS_GML2; /* default output is GML 2.1 */
outputFormatObj *psFormat = NULL;
char **aFIDLayers = NULL;
char **aFIDValues = NULL;
int iFIDLayers = 0;
int iNumberOfFeatures = 0;
int iResultTypeHits = 0;
char **papszPropertyName = NULL;
int nPropertyNames = 0;
int nQueriedLayers=0;
layerObj *lpQueried=NULL;
/*use msLayerGetShape instead of msLayerResultsGetShape of complex filter #3305*/
int bComplexFilter = MS_FALSE;
/* Initialize gml options */
memset( &gmlinfo, 0, sizeof(gmlinfo) );
gmlinfo.user_namespace_prefix = "ms";
gmlinfo.user_namespace_uri = "http://mapserver.gis.umn.edu/mapserver";
gmlinfo.collection_name = OWS_WFS_FEATURE_COLLECTION_NAME;
gmlinfo.typename = "";
gmlinfo.output_schema_format = "XMLSCHEMA";
/* Default filter is map extents */
bbox = map->extent;
/* Read CGI parameters */
/* */
/* __TODO__ Need to support XML encoded requests */
/* */
if (paramsObj->pszResultType != NULL)
{
if (strcasecmp(paramsObj->pszResultType, "hits") == 0)
iResultTypeHits = 1;
}
/* typename is mandatory unlsess featureid is specfied. We do not
support featureid */
if (paramsObj->pszTypeName==NULL && paramsObj->pszFeatureId == NULL)
{
msSetError(MS_WFSERR,
"Incomplete WFS request: TYPENAME parameter missing",
"msWFSGetFeature()");
return msWFSException(map, "typename", "MissingParameterValue", paramsObj->pszVersion);
}
if(paramsObj->pszTypeName) {
int j, k, y,z;
char **tokens;
int n=0, i=0;
char szTmp[256];
const char *pszFullName = NULL;
/* keep a ref for layer use. */
gmlinfo.typename = paramsObj->pszTypeName;
/* Parse comma-delimited list of type names (layers) */
/* */
/* __TODO__ Need to handle type grouping, e.g. "(l1,l2),l3,l4" */
/* */
layers = msStringSplit(gmlinfo.typename, ',', &numlayers);
/* ==================================================================== */
/* TODO: check if the typename contains namespaces (ex cdf:Other), */
/* If that is the case extract only the layer name. */
/* ==================================================================== */
if (layers==NULL || numlayers < 1) {
msSetError(MS_WFSERR, "At least one type name required in TYPENAME parameter.", "msWFSGetFeature()");
return msWFSException(map, "typename", "InvalidParameterValue", paramsObj->pszVersion);
}
tokens = msStringSplit(layers[0], ':', &n);
if (tokens && n==2 && msGetLayerIndex(map, layers[0]) < 0) {
msFreeCharArray(tokens, n);
for (i=0; inumlayers; j++) {
layerObj *lp;
lp = GET_LAYER(map, j);
/* Keep only selected layers, set to OFF by default. */
lp->status = MS_OFF;
}
for (k=0; knumlayers; j++) {
layerObj *lp;
char *pszPropertyName = NULL;
lp = GET_LAYER(map, j);
if (msWFSIsLayerSupported(lp) && lp->name && (strcasecmp(lp->name, layers[k]) == 0) &&
(msIntegerInArray(lp->index, ows_request->enabled_layers, ows_request->numlayers)) ) {
bLayerFound = MS_TRUE;
lp->status = MS_ON;
if (lp->template == NULL) {
/* Force setting a template to enable query. */
lp->template = msStrdup("ttt.html");
}
/* set the gml_include_items METADATA */
if (paramsObj->pszPropertyName)
{
pszPropertyName = paramsObj->pszPropertyName;
/*we parse the propertyname parameter only once*/
if (papszPropertyName == NULL)
{
if (strlen(pszPropertyName) > 0 && (pszPropertyName[0] == '(' || numlayers == 1))
{
if (numlayers == 1 && pszPropertyName[0] != '(')
{
/* Accept PROPERTYNAME without () when there is a single TYPENAME */
char* pszTmpPropertyName = msSmallMalloc(1+strlen(pszPropertyName)+1+1);
sprintf(pszTmpPropertyName, "(%s)", pszPropertyName);
tokens = msStringSplit(pszTmpPropertyName+1, '(', &nPropertyNames);
free(pszTmpPropertyName);
}
else
tokens = msStringSplit(pszPropertyName+1, '(', &nPropertyNames);
/*expecting to always have a list of property names equal to
the number of layers(typename)*/
if (nPropertyNames != numlayers)
{
if (tokens)
msFreeCharArray(tokens, nPropertyNames);
msSetError(MS_WFSERR,
"Optional PROPERTYNAME parameter. A list of properties may be specified for each type name. Example TYPENAME=name1&name2&PROPERTYNAME=(prop1,prop2)(prop1)",
"msWFSGetFeature()");
return msWFSException(map, "PROPERTYNAME", "InvalidParameterValue", paramsObj->pszVersion);
}
papszPropertyName = (char **)msSmallMalloc(sizeof(char *)*nPropertyNames);
for (i=0; i 0)
{
/*trim namespaces. PROPERTYNAME=(ns:prop1,ns:prop2)(prop1)*/
if (strstr(tokens[i], ":"))
{
char **tokens1, **tokens2;
int n1=0,n2=0,l=0;
char *pszTmp = NULL;
tokens1 = msStringSplit(tokens[i], ',', &n1);
for (l=0;l0)
msFreeCharArray(tokens2, n2);
}
else
pszTmp = msStringConcatenate(pszTmp,tokens1[l]);
}
papszPropertyName[i] = msStrdup(pszTmp);
msFree(pszTmp);
if (tokens1 && n1>0)
msFreeCharArray(tokens1, n1);
}
else
papszPropertyName[i] = msStrdup(tokens[i]);
/* remove trailing ) */
papszPropertyName[i][strlen(papszPropertyName[i])-1] = '\0';
}
else
papszPropertyName[i] = NULL; /*should return an error*/
}
if (tokens)
msFreeCharArray(tokens, nPropertyNames);
}
else
{
msSetError(MS_WFSERR,
"Optional PROPERTYNAME parameter. A list of properties may be specified for each type name. Example TYPENAME=name1&name2&PROPERTYNAME=(prop1,prop2)(prop1)",
"msWFSGetFeature()");
return msWFSException(map, "PROPERTYNAME", "InvalidParameterValue", paramsObj->pszVersion);
}
}
/*do an alias replace for the current layer*/
if (papszPropertyName && msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS)
{
for(z=0; znumitems; z++)
{
if (!lp->items[z] || strlen(lp->items[z]) <= 0)
continue;
snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[z]);
pszFullName = msOWSLookupMetadata(&(lp->metadata), "G", szTmp);
if (pszFullName)
papszPropertyName[k] = msReplaceSubstring(papszPropertyName[k], pszFullName, lp->items[z]);
}
/*validate that the property names passed are part of the items list*/
tokens = msStringSplit(papszPropertyName[k], ',', &n);
for (y=0; y 0)
{
if (strcasecmp(tokens[y], "*") == 0 ||
strcasecmp(tokens[y], "!") == 0)
continue;
for(z=0; znumitems; z++)
{
if (strcasecmp(tokens[y], lp->items[z]) == 0)
break;
}
/*we need to check of the property name is the geometry name; In that case it
is a valid property name*/
if (msOWSLookupMetadata(&(lp->metadata), "OFG", "geometries") != NULL)
snprintf(szTmp, sizeof(szTmp), "%s", msOWSLookupMetadata(&(lp->metadata), "OFG", "geometries"));
else
snprintf(szTmp, sizeof(szTmp), OWS_GML_DEFAULT_GEOMETRY_NAME);
if (z == lp->numitems && strcasecmp(tokens[y], szTmp) != 0)
{
msSetError(MS_WFSERR,
"Invalid PROPERTYNAME %s", "msWFSGetFeature()", tokens[y]);
msFreeCharArray(tokens, n);
return msWFSException(map, "PROPERTYNAME", "InvalidParameterValue", paramsObj->pszVersion);
}
}
}
if (tokens && n > 0)
msFreeCharArray(tokens, n);
msLayerClose(lp);
}
if (papszPropertyName)
{
if (strlen(papszPropertyName[k]) > 0)
{
if (strcasecmp(papszPropertyName[k], "*") == 0)
{
msInsertHashTable(&(lp->metadata), "GML_INCLUDE_ITEMS", "all");
}
/*this character is only used internally and allows postrequest to
have a proper property name parsing. It means do not affect what was set
in the map file, It is set necessary when a wfs post request is used with
several query elements, with some having property names and some not*/
else if (strcasecmp(papszPropertyName[k], "!") == 0)
{
}
else
{
msInsertHashTable(&(lp->metadata), "GML_INCLUDE_ITEMS", papszPropertyName[k]);
/* exclude geometry if it was not asked for */
if (msOWSLookupMetadata(&(lp->metadata), "OFG", "geometries") != NULL)
snprintf(szTmp, sizeof(szTmp), "%s", msOWSLookupMetadata(&(lp->metadata), "OFG", "geometries"));
else
snprintf(szTmp, sizeof(szTmp), OWS_GML_DEFAULT_GEOMETRY_NAME);
if (strstr(papszPropertyName[k], szTmp) == NULL)
msInsertHashTable(&(lp->metadata), "GML_GEOMETRIES", "none");
}
}
else /*empty string*/
msInsertHashTable(&(lp->metadata), "GML_GEOMETRIES", "none");
}
}
}
}
if (!bLayerFound) {
/* Requested layer name was not in capabilities:
* either it just doesn't exist
*/
msSetError(MS_WFSERR,
"TYPENAME '%s' doesn't exist in this server. Please check the capabilities and reformulate your request.",
"msWFSGetFeature()", layers[k]);
return msWFSException(map, "typename", "InvalidParameterValue", paramsObj->pszVersion);
}
}
}
if (papszPropertyName && nPropertyNames > 0)
{
for (i=0; ipszOutputFormat) {
/* We support only GML2 and GML3 for now. */
if(strcasecmp(paramsObj->pszOutputFormat, "GML2") == 0 || strcasecmp(paramsObj->pszOutputFormat, "text/xml; subtype=gml/2.1.2") == 0) {
outputformat = OWS_GML2;
gmlinfo.output_schema_format = "XMLSCHEMA";
output_mime_type = "text/xml; subtype=gml/2.1.2";
} else if(strcasecmp(paramsObj->pszOutputFormat, "GML3") == 0 || strcasecmp(paramsObj->pszOutputFormat, "text/xml; subtype=gml/3.1.1") == 0) {
outputformat = OWS_GML3;
gmlinfo.output_schema_format = "SFE_XMLSCHEMA";
output_mime_type = "text/xml; subtype=gml/3.1.1";
} else {
const char *format_list;
hashTableObj *md;
/* validate selected format against all selected layers. */
for(j=0; j < map->numlayers; j++) {
layerObj *lp;
lp = GET_LAYER(map, j);
if( lp->status != MS_ON )
continue;
md = &(lp->metadata);
format_list = msOWSLookupMetadata(md, "F","getfeature_formatlist");
if( format_list == NULL )
{
md = &(map->web.metadata);
format_list = msOWSLookupMetadata(md, "F","getfeature_formatlist");
}
if (format_list)
{
psFormat = msOwsIsOutputFormatValid(
map, paramsObj->pszOutputFormat, md,
"F", "getfeature_formatlist");
}
if (psFormat == NULL)
{
msSetError(MS_WFSERR,
"'%s' is not a permitted output format for layer '%s', review wfs_formats setting.",
"msWFSGetFeature()",
paramsObj->pszOutputFormat,
lp->name );
return msWFSException(map, "outputformat",
"InvalidParameterValue",
paramsObj->pszVersion );
}
if( psFormat->imagemode != MS_IMAGEMODE_FEATURE )
{
msSetError(MS_WFSERR,
"OUTPUTFORMAT '%s' does not have IMAGEMODE FEATURE, and is not permitted for WFS output.",
"msWFSGetFeature()",
paramsObj->pszOutputFormat );
return msWFSException( map, "outputformat",
"InvalidParameterValue",
paramsObj->pszVersion );
}
}
}
/* If OUTPUTFORMAT not set, default to gml */
} else {
/*set the output format using the version if available*/
if(paramsObj->pszVersion == NULL || strncmp(paramsObj->pszVersion,"1.1",3) == 0 )
{
outputformat = OWS_GML3;
gmlinfo.output_schema_format = "text/xml;%20subtype=gml/3.1.1";
output_mime_type = "text/xml; subtype=gml/3.1.1";
}
}
if(strncmp(paramsObj->pszVersion,"1.0",3) == 0 ) {
output_mime_type = "text/xml";
}
/* else if (strcasecmp(names[i], "PROPERTYNAME") == 0) */
/* { */
/* */
/* } */
tmpmaxfeatures = msOWSLookupMetadata(&(map->web.metadata), "FO", "maxfeatures");
if (tmpmaxfeatures)
maxfeatures = atoi(tmpmaxfeatures);
if (paramsObj->nMaxFeatures > 0) {
if (maxfeatures < 0 || (maxfeatures > 0 && paramsObj->nMaxFeatures < maxfeatures))
maxfeatures = paramsObj->nMaxFeatures;
}
nQueriedLayers=0;
for(j=0; jnumlayers; j++)
{
layerObj *lp;
lp = GET_LAYER(map, j);
if (lp->status == MS_ON)
{
lpQueried = GET_LAYER(map, j);
nQueriedLayers++;
}
}
if (paramsObj->nStartIndex > 0)
startindex = paramsObj->nStartIndex;
/*maxfeatures set but no startindex*/
if (maxfeatures > 0 && startindex < 0)
{
for(j=0; jnumlayers; j++)
{
layerObj *lp;
lp = GET_LAYER(map, j);
if (lp->status == MS_ON)
{
/*over-ride the value only if it is unset or wfs maxfeattures is
lower that what is currently set*/
if (lp->maxfeatures <=0 || (lp->maxfeatures > 0 && maxfeatures < lp->maxfeatures))
lp->maxfeatures = maxfeatures;
}
}
}
/*no maxfeatures set but startindex set*/
if (maxfeatures <=0 && startindex > 0)
{
if (nQueriedLayers == 1 && msLayerSupportsPaging(lpQueried))
{
lpQueried->startindex = startindex;
startindex = -1;
}
}
/*maxfeatures set and startindex set*/
if (maxfeatures >0 && startindex > 0)
{
if (nQueriedLayers == 1 && msLayerSupportsPaging(lpQueried))
{
lpQueried->startindex = startindex;
if (lpQueried->maxfeatures <=0 ||
(lpQueried->maxfeatures > 0 && maxfeatures < lpQueried->maxfeatures))
lpQueried->maxfeatures = maxfeatures;
startindex = -1;
maxfeatures = -1;
}
}
if (paramsObj->pszFilter) {
bFilterSet = 1;
pszFilter = paramsObj->pszFilter;
}
if (paramsObj->pszBbox) {
char **tokens;
int n;
tokens = msStringSplit(paramsObj->pszBbox, ',', &n);
if (tokens==NULL || (n != 4 && n !=5)) {
msSetError(MS_WFSERR, "Wrong number of arguments for BBOX.", "msWFSGetFeature()");
return msWFSException(map, "bbox", "InvalidParameterValue", paramsObj->pszVersion);
}
bbox.minx = atof(tokens[0]);
bbox.miny = atof(tokens[1]);
bbox.maxx = atof(tokens[2]);
bbox.maxy = atof(tokens[3]);
/*5th aregument is assumed to be projection*/
if (n == 5)
sBBoxSrs = msStrdup(tokens[4]);
msFreeCharArray(tokens, n);
bBBOXSet = 1;
/* Note: BBOX SRS is implicit, it is the SRS of the selected */
/* feature types, see pszOutputSRS in TYPENAMES above. */
}
if (paramsObj->pszFeatureId) {
bFeatureIdSet = 1;
}
#ifdef USE_OGR
if (bFilterSet && pszFilter && strlen(pszFilter) > 0) {
char **tokens = NULL;
int nFilters;
FilterEncodingNode *psNode = NULL;
int iLayerIndex =1;
char **paszFilter = NULL;
errorObj *ms_error;
/* -------------------------------------------------------------------- */
/* Validate the parameters. When a FILTER parameter is given, */
/* It needs the TYPENAME parameter for the layers. Also Filter */
/* is Mutually exclusive with FEATUREID and BBOX (see wfs specs */
/* 1.0 section 13.7.3 on GetFeature) */
/* */
/* -------------------------------------------------------------------- */
if (gmlinfo.typename == NULL || strlen(gmlinfo.typename) <= 0 || layers == NULL || numlayers <= 0) {
msSetError(MS_WFSERR,
"Required TYPENAME parameter missing for GetFeature with a FILTER parameter.",
"msWFSGetFeature()");
return msWFSException(map, "typename", "MissingParameterValue", paramsObj->pszVersion);
}
if (bBBOXSet) {
msSetError(MS_WFSERR,
"BBOX parameter and FILTER parameter are mutually exclusive in GetFeature.",
"msWFSGetFeature()");
return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion);
}
if (bFeatureIdSet) {
msSetError(MS_WFSERR,
"FEATUREID parameter and FILTER parameter are mutually exclusive in GetFeature.",
"msWFSGetFeature()");
return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion);
}
/* -------------------------------------------------------------------- */
/* Parse the Filter parameter. If there are several Filter */
/* parameters, each Filter is inside a parantheses. Eg : */
/* FILTER=( */
/* INWATERA_1M/WKB_GEOM|INWATERA_1M/WKB_GEOM */
/* 10,10 20,20*/
/* )( */
/* INWATERA_1M/WKB_GEOM10,10*/
/* 20,20) */
/* -------------------------------------------------------------------- */
nFilters = 0;
if (strlen(pszFilter) > 0 && pszFilter[0] == '(')
{
tokens = msStringSplit(pszFilter+1, '(', &nFilters);
if (tokens && nFilters > 0 && numlayers == nFilters)
{
paszFilter = (char **)msSmallMalloc(sizeof(char *)*nFilters);
for (i=0; ipszVersion);
}
/* -------------------------------------------------------------------- */
/* run through the filters and build the class expressions. */
/* TODO: items may have namespace prefixes, or reference aliases, */
/* or groups. Need to be a bit more sophisticated here. */
/* -------------------------------------------------------------------- */
for (i=0; ipszVersion);
}
psNode = FLTParseFilterEncoding(paszFilter[i]);
/*if we have a complex filter, make sure that paging is done at the gml output level
and not at the driver level #3305*/
bComplexFilter = (!FLTIsSimpleFilter(psNode));
if (bComplexFilter && nQueriedLayers == 1 && lpQueried &&
msLayerSupportsPaging(lpQueried) &&
(lpQueried->startindex > 0 && lpQueried->maxfeatures > 0))
{
startindex = lpQueried->startindex;
lpQueried->startindex = -1;
maxfeatures = lpQueried->maxfeatures;
lpQueried->maxfeatures = -1;
}
if (!psNode) {
msSetError(MS_WFSERR,
"Invalid or Unsupported FILTER in GetFeature : %s",
"msWFSGetFeature()", pszFilter);
return msWFSException(map, "filter", "InvalidParameterValue", paramsObj->pszVersion);
}
/*preparse the filter for gml aliases*/
FLTPreParseFilterForAlias(psNode, map, iLayerIndex, "G");
/* run filter. If no results are found, do not throw exception */
/* this is a null result */
if( FLTApplyFilterToLayer(psNode, map, iLayerIndex) != MS_SUCCESS )
{
ms_error = msGetErrorObj();
if(ms_error->code != MS_NOTFOUND)
{
msSetError(MS_WFSERR, "FLTApplyFilterToLayer() failed", "msWFSGetFeature()", pszFilter);
return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion);
}
}
FLTFreeFilterEncodingNode( psNode );
psNode = NULL;
}
if (paszFilter)
free(paszFilter);
}/* end if filter set */
if (bFeatureIdSet)
{
char **tokens = NULL, **tokens1=NULL ;
int nTokens = 0, j=0, nTokens1=0, k=0;
FilterEncodingNode *psNode = NULL;
/* Keep only selected layers, set to OFF by default. */
for(j=0; jnumlayers; j++)
{
layerObj *lp;
lp = GET_LAYER(map, j);
lp->status = MS_OFF;
}
/*featureid can be a list INWATERA_1M.1234, INWATERA_1M.1235
We will keep all the feature id from the same layer together
so that an OR would be applied if several of them are present
*/
tokens = msStringSplit(paramsObj->pszFeatureId, ',', &nTokens);
iFIDLayers = 0;
if (tokens && nTokens >=1)
{
aFIDLayers = (char **)msSmallMalloc(sizeof(char *)*nTokens);
aFIDValues = (char **)msSmallMalloc(sizeof(char *)*nTokens);
for (j=0; jpszVersion);
}
if (tokens1)
msFreeCharArray(tokens1, nTokens1);
}
}
if (tokens)
msFreeCharArray(tokens, nTokens);
/*turn on the layers and make sure projections are set properly*/
for (j=0; j< iFIDLayers; j++)
{
for (k=0; knumlayers; k++)
{
layerObj *lp;
lp = GET_LAYER(map, k);
if (msWFSIsLayerSupported(lp) && lp->name &&
strcasecmp(lp->name, aFIDLayers[j]) == 0)
{
lp->status = MS_ON;
}
}
if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, paramsObj->pszVersion) == MS_FAILURE)
return msWFSException(map, "typename", "InvalidParameterValue", paramsObj->pszVersion);
}
for (j=0; j< iFIDLayers; j++)
{
for (k=0; knumlayers; k++)
{
layerObj *lp;
lp = GET_LAYER(map, k);
if (msWFSIsLayerSupported(lp) && lp->name &&
strcasecmp(lp->name, aFIDLayers[j]) == 0)
{
lp->status = MS_ON;
if (lp->template == NULL) {
/* Force setting a template to enable query. */
lp->template = msStrdup("ttt.html");
}
psNode = FLTCreateFeatureIdFilterEncoding(aFIDValues[j]);
if( FLTApplyFilterToLayer(psNode, map, lp->index) != MS_SUCCESS ) {
msSetError(MS_WFSERR, "FLTApplyFilterToLayer() failed", "msWFSGetFeature");
return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion);
}
FLTFreeFilterEncodingNode( psNode );
psNode = NULL;
break;
}
}
if (k == map->numlayers)/*layer not found*/
{
msSetError(MS_WFSERR,
"Invalid typename given with FeatureId in GetFeature : %s", "msWFSGetFeature()",
aFIDLayers[j]);
if (aFIDLayers && aFIDValues)
{
for (j=0; jpszVersion);
}
}
if (aFIDLayers && aFIDValues)
{
for (j=0; jpszSrs, paramsObj->pszVersion) == MS_FAILURE)
return msWFSException(map, "typename", "InvalidParameterValue", paramsObj->pszVersion);
/*
** Perform Query (only BBOX for now)
*/
/* __TODO__ Using a rectangle query may not be the most efficient way */
/* to do things here. */
if (!bFilterSet && !bFeatureIdSet) {
if (!bBBOXSet)
{
const char *pszMapSRS=NULL, *pszLayerSRS=NULL;
bbox = map->extent;
map->query.type = MS_QUERY_BY_RECT; /* setup the query */
map->query.mode = MS_QUERY_MULTIPLE;
/*if srsName was given for wfs 1.1.0, It is at this point loaded into the
map object and should be used*/
if(!paramsObj->pszSrs)
pszMapSRS = msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE);
for(j=0; jnumlayers; j++)
{
layerObj *lp;
rectObj ext;
int status;
lp = GET_LAYER(map, j);
if (lp->status == MS_ON)
{
if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS)
{
if (pszMapSRS != NULL && strncmp(pszMapSRS, "EPSG:", 5) == 0) {
if( msOWSParseVersionString(paramsObj->pszVersion) >= OWS_1_1_0 )
status = msLoadProjectionStringEPSG(&(map->projection), pszMapSRS);
else
status = msLoadProjectionString(&(map->projection), pszMapSRS);
if (status != 0) {
msSetError(MS_WFSERR, "msLoadProjectionString() failed: %s",
"msWFSGetFeature()", pszMapSRS);
return msWFSException(map, "mapserv", "NoApplicableCode",
paramsObj->pszVersion);
}
}
/*make sure that the layer projectsion is loaded.
It could come from a ows/wfs_srs metadata*/
if (lp->projection.numargs == 0)
{
pszLayerSRS = msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE);
if (pszLayerSRS)
{
if (strncmp(pszLayerSRS, "EPSG:", 5) == 0)
{
if( msOWSParseVersionString(paramsObj->pszVersion) >= OWS_1_1_0 )
msLoadProjectionStringEPSG(&(lp->projection), pszLayerSRS);
else
msLoadProjectionString(&(lp->projection), pszLayerSRS);
}
}
}
if (msProjectionsDiffer(&map->projection, &lp->projection) == MS_TRUE) {
msProjectRect(&lp->projection, &map->projection, &(ext));
}
bbox = ext;
}
map->query.rect = bbox;
map->query.layer = j;
if(msQueryByRect(map) != MS_SUCCESS)
{
errorObj *ms_error;
ms_error = msGetErrorObj();
if(ms_error->code != MS_NOTFOUND) {
msSetError(MS_WFSERR, "ms_error->code not found", "msWFSGetFeature()");
return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion);
}
}
}
}
}
else
{
if (sBBoxSrs)
{
int status;
projectionObj sProjTmp;
msInitProjection(&sProjTmp);
/*do the axis order for now. It is unclear if the bbox are expected
ro respect the axis oder defined in the projectsion #3296*/
if(strncmp(paramsObj->pszVersion,"1.1",3) == 0)
{
if ((status=msLoadProjectionStringEPSG(&sProjTmp, sBBoxSrs)) == 0)
{
msAxisNormalizePoints( &sProjTmp, 1, &bbox.minx, &bbox.miny );
msAxisNormalizePoints( &sProjTmp, 1, &bbox.maxx, &bbox.maxy );
}
}
else
status = msLoadProjectionString(&sProjTmp, sBBoxSrs);
if (status == 0 && map->projection.numargs > 0)
msProjectRect(&sProjTmp, &map->projection, &bbox);
msFree(sBBoxSrs);
}
map->query.type = MS_QUERY_BY_RECT; /* setup the query */
map->query.mode = MS_QUERY_MULTIPLE;
map->query.rect = bbox;
if(msQueryByRect(map) != MS_SUCCESS) {
errorObj *ms_error;
ms_error = msGetErrorObj();
if(ms_error->code != MS_NOTFOUND) {
msSetError(MS_WFSERR, "ms_error->code not found", "msWFSGetFeature()");
return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion);
}
}
}
}
/* if no results where written (TODO: this needs to be GML2/3 specific I imagine */
for(j=0; jnumlayers; j++) {
if (GET_LAYER(map, j)->resultcache && GET_LAYER(map, j)->resultcache->numresults > 0)
{
iNumberOfFeatures += GET_LAYER(map, j)->resultcache->numresults;
}
}
if(maxfeatures > 0 && maxfeatures < iNumberOfFeatures)
iNumberOfFeatures = maxfeatures;
/*
** GML Header generation.
*/
status = MS_SUCCESS;
if( psFormat == NULL )
{
value = msOWSLookupMetadata(&(map->web.metadata), "FO", "encoding");
if (value)
msIO_setHeader("Content-type","%s; charset=%s", output_mime_type,value);
else
msIO_setHeader("Content-type",output_mime_type);
msIO_sendHeaders();
msWFSGetFeature_GMLPreamble( map, req, &gmlinfo, paramsObj,
outputformat,
iResultTypeHits,
iNumberOfFeatures );
}
/* handle case of maxfeatures = 0 */
/*internally use a start index that start with with 0 as the first index*/
if( psFormat == NULL )
{
if(maxfeatures != 0 && iResultTypeHits == 0)
status = msGMLWriteWFSQuery(map, stdout, startindex-1, maxfeatures,
(char *) gmlinfo.user_namespace_prefix,
outputformat);
}
else
{
int to_allow = maxfeatures, to_skip = startindex-1;
mapservObj *mapserv = msAllocMapServObj();
/* Setup dummy mapserv object */
mapserv->sendheaders = MS_TRUE;
mapserv->map = map;
msFreeCgiObj(mapserv->request);
mapserv->request = req;
map->querymap.status = MS_FALSE;
/* trim the query result(s) if maxfeatures or startindex set. */
for( j=0; j < map->numlayers; j++ ) {
layerObj *lp = GET_LAYER(map, j);
if (lp->resultcache && lp->resultcache->numresults > 0)
{
if( to_skip > 0 && lp->resultcache->numresults < to_skip )
{
to_skip -= lp->resultcache->numresults;
lp->resultcache->numresults = 0;
}
else if( to_skip > 0 )
{
memmove( lp->resultcache->results + 0,
lp->resultcache->results + to_skip,
sizeof(resultObj) * (lp->resultcache->numresults - to_skip) );
lp->resultcache->numresults -= to_skip;
to_skip = 0;
}
if( maxfeatures > 0 )
{
if( lp->resultcache->numresults > to_allow )
{
lp->resultcache->numresults = to_allow;
to_allow = 0;
}
else
{
to_allow -= lp->resultcache->numresults;
if( to_allow < 0 )
to_allow = 0;
lp->resultcache->numresults = 0;
}
}
}
}
status = msReturnTemplateQuery( mapserv, psFormat->name, NULL );
mapserv->request = NULL;
mapserv->map = NULL;
msFreeMapServObj( mapserv );
if( status != MS_SUCCESS )
{
return msWFSException(map, "mapserv", "NoApplicableCode",
paramsObj->pszVersion );
}
}
if( psFormat == NULL && status == MS_SUCCESS )
{
msWFSGetFeature_GMLPostfix( map, req, &gmlinfo, paramsObj,
outputformat,
maxfeatures, iResultTypeHits, iNumberOfFeatures );
}
/*
** Done! Now a bit of clean-up.
*/
return status;
}
#endif /* USE_WFS_SVR */
/*
** msWFSDispatch() is the entry point for WFS requests.
** - If this is a valid request then it is processed and MS_SUCCESS is returned
** on success, or MS_FAILURE on failure.
** - If this does not appear to be a valid WFS request then MS_DONE
** is returned and MapServer is expected to process this as a regular
** MapServer request.
*/
int msWFSDispatch(mapObj *map, cgiRequestObj *requestobj, owsRequestObj *ows_request, int force_wfs_mode)
{
#ifdef USE_WFS_SVR
int status;
int returnvalue = MS_DONE;
/* static char *wmtver = NULL, *request=NULL, *service=NULL; */
wfsParamsObj *paramsObj;
/*
** Populate the Params object based on the request
*/
paramsObj = msWFSCreateParamsObj();
/* TODO : store also parameters that are inside the map object */
/* into the paramsObj. */
if (msWFSParseRequest(map, requestobj, ows_request, paramsObj, force_wfs_mode) == MS_FAILURE)
return msWFSException(map, "request", "InvalidRequest", NULL);
if (force_wfs_mode)
{
/*request is always required*/
if (paramsObj->pszRequest==NULL || strlen(paramsObj->pszRequest)<=0)
{
msSetError(MS_WFSERR,
"Incomplete WFS request: REQUEST parameter missing",
"msWFSDispatch()");
returnvalue = msWFSException(map, "request", "MissingParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
/*version:
wfs 1.0 and 1.1.0 POST request: optional
wfs 1.0 and 1.1.0 GET request: optional for getcapabilities and required for describefeatute and getfeature
*/
if (paramsObj->pszVersion == NULL && requestobj && requestobj->postrequest == MS_FALSE &&
strcasecmp(paramsObj->pszRequest, "GetCapabilities") != 0)
{
msSetError(MS_WFSERR,
"Invalid WFS request: VERSION parameter missing",
"msWFSDispatch()");
returnvalue = msWFSException(map, "version", "MissingParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
if (paramsObj->pszVersion == NULL || strlen(paramsObj->pszVersion) <=0)
paramsObj->pszVersion = msStrdup("1.1.0");
/*service
wfs 1.0 and 1.1.0 GET: required and should be set to WFS
wfs 1.0 POST: required
wfs 1.1.1 POST: optional
*/
if ((paramsObj->pszService == NULL || strlen(paramsObj->pszService) == 0) &&
((requestobj && requestobj->postrequest == MS_FALSE) || strcasecmp(paramsObj->pszVersion,"1.0") ==0))
{
msSetError(MS_WFSERR,
"Invalid WFS request: Missing SERVICE parameter",
"msWFSDispatch()");
returnvalue = msWFSException(map, "service", "MissingParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
if (paramsObj->pszService == NULL || strlen(paramsObj->pszService) == 0)
paramsObj->pszService = msStrdup("WFS");
if (paramsObj->pszService!=NULL && strcasecmp(paramsObj->pszService, "WFS") != 0)
{
msSetError(MS_WFSERR,
"Invalid WFS request: SERVICE parameter must be set to WFS",
"msWFSDispatch()");
returnvalue = msWFSException(map, "service", "InvalidParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
if (paramsObj->pszService == NULL && strcasecmp(paramsObj->pszVersion, "1.0") == 0)
{
msSetError(MS_WFSERR,
"Invalid WFS request: SERVICE parameter missing",
"msWFSDispatch()");
returnvalue = msWFSException(map, "service", "MissingParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
}
/* If SERVICE is specified then it MUST be "WFS" */
if (paramsObj->pszService != NULL &&
strcasecmp(paramsObj->pszService, "WFS") != 0)
{
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return MS_DONE; /* Not a WFS request */
}
/* If SERVICE, VERSION and REQUEST not included than this isn't a WFS req*/
if (paramsObj->pszService == NULL && paramsObj->pszVersion==NULL &&
paramsObj->pszRequest==NULL)
{
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return MS_DONE; /* Not a WFS request */
}
/* VERSION *and* REQUEST *and* SERVICE required by all WFS requests including
* GetCapabilities.
*/
if (paramsObj->pszVersion==NULL || strlen(paramsObj->pszVersion)<=0)
{
msSetError(MS_WFSERR,
"Incomplete WFS request: VERSION parameter missing",
"msWFSDispatch()");
returnvalue = msWFSException11(map, "version", "MissingParameterValue", "1.1.0");
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
if (paramsObj->pszRequest==NULL || strlen(paramsObj->pszRequest)<=0)
{
msSetError(MS_WFSERR,
"Incomplete WFS request: REQUEST parameter missing",
"msWFSDispatch()");
returnvalue = msWFSException(map, "request", "MissingParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
if (paramsObj->pszService==NULL || strlen(paramsObj->pszService)<=0)
{
msSetError(MS_WFSERR,
"Incomplete WFS request: SERVICE parameter missing",
"msWFSDispatch()");
returnvalue = msWFSException(map, "service", "MissingParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
if ((status = msOWSMakeAllLayersUnique(map)) != MS_SUCCESS)
{
msSetError(MS_WFSERR, "msOWSMakeAllLayersUnique() failed", "msWFSDispatch()");
returnvalue = msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
/*
** Start dispatching requests
*/
if (strcasecmp(paramsObj->pszRequest, "GetCapabilities") == 0 )
{
msOWSRequestLayersEnabled(map, "F", paramsObj->pszRequest, ows_request);
if (ows_request->numlayers == 0)
{
msSetError(MS_WFSERR, "WFS request not enabled. Check wfs/ows_enable_request settings.", "msWFSDispatch()");
returnvalue = msWFSException(map, "request", "InvalidParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
returnvalue = msWFSGetCapabilities(map, paramsObj, requestobj, ows_request);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
/*
** Validate VERSION against the versions that we support... we don't do this
** for GetCapabilities in order to allow version negociation.
*/
if (strcmp(paramsObj->pszVersion, "1.0.0") != 0 &&
strcmp(paramsObj->pszVersion, "1.1.0") != 0)
{
msSetError(MS_WFSERR,
"WFS Server does not support VERSION %s.",
"msWFSDispatch()", paramsObj->pszVersion);
returnvalue = msWFSException11(map, "version", "InvalidParameterValue","1.1.0");
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
/* Since the function can still return MS_DONE, we add an extra service check to not call
msOWSRequestLayersEnabled twice */
if (strcasecmp(paramsObj->pszService, "WFS") == 0)
{
msOWSRequestLayersEnabled(map, "F", paramsObj->pszRequest, ows_request);
if (ows_request->numlayers == 0)
{
msSetError(MS_WFSERR, "WFS request not enabled. Check wfs/ows_enable_request settings.", "msWFSDispatch()");
returnvalue = msWFSException(map, "request", "InvalidParameterValue", paramsObj->pszVersion);
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
}
}
returnvalue = MS_DONE;
/* Continue dispatching...
*/
if (strcasecmp(paramsObj->pszRequest, "DescribeFeatureType") == 0)
returnvalue = msWFSDescribeFeatureType(map, paramsObj, ows_request);
else if (strcasecmp(paramsObj->pszRequest, "GetFeature") == 0)
returnvalue = msWFSGetFeature(map, paramsObj, requestobj, ows_request);
else if (strcasecmp(paramsObj->pszRequest, "GetFeatureWithLock") == 0 ||
strcasecmp(paramsObj->pszRequest, "LockFeature") == 0 ||
strcasecmp(paramsObj->pszRequest, "Transaction") == 0 )
{
/* Unsupported WFS request */
msSetError(MS_WFSERR, "Unsupported WFS request: %s", "msWFSDispatch()",
paramsObj->pszRequest);
returnvalue = msWFSException(map, "request", "InvalidParameterValue", paramsObj->pszVersion);
}
else if (strcasecmp(paramsObj->pszService, "WFS") == 0)
{
/* Invalid WFS request */
msSetError(MS_WFSERR, "Invalid WFS request: %s", "msWFSDispatch()",
paramsObj->pszRequest);
returnvalue = msWFSException(map, "request", "InvalidParameterValue", paramsObj->pszVersion);
}
/* This was not detected as a WFS request... let MapServer handle it */
msWFSFreeParamsObj(paramsObj);
free(paramsObj);
paramsObj = NULL;
return returnvalue;
#else
msSetError(MS_WFSERR, "WFS server support is not available.",
"msWFSDispatch()");
return(MS_FAILURE);
#endif
}
/************************************************************************/
/* msWFSCreateParamsObj */
/* */
/* Create a parameter object, initialize it. */
/* The caller should free the object using msWFSFreeParamsObj. */
/************************************************************************/
wfsParamsObj *msWFSCreateParamsObj()
{
wfsParamsObj *paramsObj = (wfsParamsObj *)calloc(1, sizeof(wfsParamsObj));
MS_CHECK_ALLOC(paramsObj, sizeof(wfsParamsObj), NULL);
paramsObj->nMaxFeatures = -1;
paramsObj->nStartIndex = -1;
return paramsObj;
}
/************************************************************************/
/* msWFSFreeParmsObj */
/* */
/* Free params object. */
/************************************************************************/
void msWFSFreeParamsObj(wfsParamsObj *wfsparams)
{
if (wfsparams)
{
free(wfsparams->pszVersion);
free(wfsparams->pszUpdateSequence);
free(wfsparams->pszRequest);
free(wfsparams->pszService);
free(wfsparams->pszTypeName);
free(wfsparams->pszFilter);
free(wfsparams->pszBbox);
free(wfsparams->pszOutputFormat);
free(wfsparams->pszFeatureId);
free(wfsparams->pszSrs);
free(wfsparams->pszResultType);
free(wfsparams->pszPropertyName);
free(wfsparams->pszAcceptVersions);
}
}
const char *msWFSGetDefaultVersion(mapObj *map)
{
if (msOWSLookupMetadata(&(map->web.metadata), "F", "getcapabilities_version"))
return msOWSLookupMetadata(&(map->web.metadata), "F", "getcapabilities_version");
else
return "1.1.0";
}
/************************************************************************/
/* msWFSParseRequest */
/* */
/* Parse request into the params object. */
/************************************************************************/
int msWFSParseRequest(mapObj *map, cgiRequestObj *request, owsRequestObj *ows_request,
wfsParamsObj *wfsparams, int force_wfs_mode)
{
#ifdef USE_WFS_SVR
int i = 0;
if (!request || !wfsparams)
return MS_FAILURE;
if (request->NumParams > 0)
{
for(i=0; iNumParams; i++)
{
if (request->ParamNames[i] && request->ParamValues[i])
{
if (strcasecmp(request->ParamNames[i], "VERSION") == 0)
wfsparams->pszVersion = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "UPDATESEQUENCE") == 0)
wfsparams->pszUpdateSequence = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "REQUEST") == 0)
wfsparams->pszRequest = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "SERVICE") == 0)
wfsparams->pszService = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "MAXFEATURES") == 0)
wfsparams->nMaxFeatures = atoi(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "STARTINDEX") == 0)
wfsparams->nStartIndex = atoi(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "BBOX") == 0)
wfsparams->pszBbox = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "SRSNAME") == 0)
wfsparams->pszSrs = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "RESULTTYPE") == 0)
wfsparams->pszResultType = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "TYPENAME") == 0)
wfsparams->pszTypeName = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "FILTER") == 0)
wfsparams->pszFilter = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "OUTPUTFORMAT") == 0)
wfsparams->pszOutputFormat = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "FEATUREID") == 0)
wfsparams->pszFeatureId = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "PROPERTYNAME") == 0)
wfsparams->pszPropertyName = msStrdup(request->ParamValues[i]);
else if (strcasecmp(request->ParamNames[i], "ACCEPTVERSIONS") == 0)
wfsparams->pszAcceptVersions = msStrdup(request->ParamValues[i]);
}
}
/* version is optional is the GetCapabilities. If not */
/* provided, set it. Default it to 1.1.0*/
if (wfsparams->pszVersion == NULL &&
wfsparams->pszRequest &&
strcasecmp(wfsparams->pszRequest, "GetCapabilities") == 0)
{
wfsparams->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
}
}
/* -------------------------------------------------------------------- */
/* Parse the post request. It is assumed to be an XML document. */
/* -------------------------------------------------------------------- */
#ifdef USE_OGR
if (request->postrequest)
{
#ifdef WFS_USE_LIBXML2
xmlDocPtr doc;
xmlNodePtr rootnode, node, node1;
char *schema_file =NULL;
const char *schema_location=NULL, *validate=NULL;
const char *pszValue=NULL;
char *pszTmp=NULL;
char *pszLayerPropertyName=NULL, *pszLayerFilter=NULL;
/* xmlXPathAxisFunc */
/* load document */
doc = xmlParseDoc((xmlChar *)request->postrequest);
if (doc == NULL || (rootnode = xmlDocGetRootElement(doc)) == NULL)
{
msSetError(MS_WFSERR, "Invalid POST request. XML is not well-formed", "msWFSParseRequest()");
return MS_FAILURE;
}
/*get the request*/
if (strcasecmp(rootnode->name, "GetCapabilities") == 0)
wfsparams->pszRequest = "GetCapabilities";
else if (strcasecmp(rootnode->name, "GetFeature") == 0)
wfsparams->pszRequest = "GetFeature";
else if (strcasecmp(rootnode->name, "DescribeFeatureType") == 0)
wfsparams->pszRequest = "DescribeFeatureType";
msOWSRequestLayersEnabled(map, "F", wfsparams->pszRequest, ows_request);
if (wfsparams->pszRequest == NULL)
{
/* Unsupported WFS request */
msSetError(MS_WFSERR, "Unsupported WFS request.", "msWFSParseRequest()");
return MS_FAILURE;
}
if (ows_request->numlayers == 0)
{
/* not enabled WFS request */
msSetError(MS_WFSERR, "WFS request not enabled. Check wfs/ows_enable_request settings.", "msWFSParseRequest()");
return MS_FAILURE;
}
/*get version and service if available*/
pszValue = xmlGetProp(rootnode, (xmlChar *)"version");
if (pszValue)
wfsparams->pszVersion = msStrdup(pszValue);
pszValue = xmlGetProp(rootnode, (xmlChar *)"service");
if (pszValue)
wfsparams->pszService = msStrdup(pszValue);
/* version is optional for the GetCapabilities. If not provided, set it*/
if (wfsparams->pszVersion == NULL &&
strcmp(wfsparams->pszRequest,"GetCapabilities") == 0)
{
wfsparams->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
}
/*do we validate the xml ?*/
validate = msOWSLookupMetadata(&(map->web.metadata), "FO", "validate_xml");
if (validate && strcasecmp(validate, "true") == 0 &&
(schema_location = msOWSLookupMetadata(&(map->web.metadata), "FO", "schemas_dir")))
{
if ((wfsparams->pszService && strcmp(wfsparams->pszService, "WFS") == 0) ||
force_wfs_mode)
{
schema_file = msStringConcatenate(schema_file, schema_location);
if (wfsparams->pszVersion == NULL ||
strncasecmp(wfsparams->pszVersion, "1.1", 3) ==0)
{
schema_file = msStringConcatenate(schema_file, "wfs/1.1.0/wfs.xsd");
if (msOWSSchemaValidation(schema_file, request->postrequest) != 0)
{
msSetError(MS_WFSERR, "Invalid POST request. XML is not valid", "msWFSParseRequest()");
return MS_FAILURE;
}
}
}
}
/*parse describefeature*/
if (strcmp(wfsparams->pszRequest,"DescribeFeatureType") == 0)
{
/*look for TypeName and outputFormat*/
for (node = rootnode->children; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE)
continue;
if (strcmp(node->name, "TypeName") == 0)
{
pszValue = xmlNodeGetContent(node);
if (wfsparams->pszTypeName == NULL)
wfsparams->pszTypeName = msStrdup(pszValue);
else
{
wfsparams->pszTypeName =
msStringConcatenate(wfsparams->pszTypeName, ",");
wfsparams->pszTypeName =
msStringConcatenate(wfsparams->pszTypeName, pszValue);
}
}
}
pszValue = xmlGetProp(rootnode, (xmlChar *)"outputFormat");
if (pszValue)
wfsparams->pszOutputFormat = msStrdup(pszValue);
}
/*parse GetFeature*/
if (strcmp(wfsparams->pszRequest,"GetFeature") == 0)
{
pszValue = xmlGetProp(rootnode, (xmlChar *)"resultType");
if (pszValue)
wfsparams->pszResultType = msStrdup(pszValue);
pszValue = xmlGetProp(rootnode, (xmlChar *)"maxFeatures");
if (pszValue)
wfsparams->nMaxFeatures = atoi(pszValue);
pszValue = xmlGetProp(rootnode, (xmlChar *)"startIndex");
if (pszValue)
wfsparams->nStartIndex = atoi(pszValue);
pszValue = xmlGetProp(rootnode, (xmlChar *)"outputFormat");
if (pszValue)
wfsparams->pszOutputFormat = msStrdup(pszValue);
/* free typename and filter. There may have been */
/* values if they were passed in the URL */
if (wfsparams->pszTypeName)
free(wfsparams->pszTypeName);
wfsparams->pszTypeName = NULL;
if (wfsparams->pszFilter)
free(wfsparams->pszFilter);
wfsparams->pszFilter = NULL;
for (node = rootnode->children; node; node = node->next)
{
if (node->type != XML_ELEMENT_NODE)
continue;
if (strcmp(node->name, "Query") == 0)
{
/* get SRS: TODO support srs per layer. Now we only have one srs
that applies to al layers*/
pszValue = xmlGetProp(node, (xmlChar *)"srsName");
if (pszValue)
{
if (wfsparams->pszSrs )
msFree( wfsparams->pszSrs);
wfsparams->pszSrs = msStrdup(pszValue);
}
/*type name*/
pszValue = xmlGetProp(node, (xmlChar *)"typeName");
if (pszValue)
{
if (wfsparams->pszTypeName == NULL)
wfsparams->pszTypeName = msStrdup(pszValue);
else
{
wfsparams->pszTypeName =
msStringConcatenate(wfsparams->pszTypeName, ",");
wfsparams->pszTypeName =
msStringConcatenate(wfsparams->pszTypeName, pszValue);
}
}
/*PropertyName and Filter*/
pszLayerPropertyName = NULL;
pszLayerFilter = NULL;
for (node1 = node->children; node1; node1 = node1->next)
{
if (node1->type != XML_ELEMENT_NODE)
continue;
if (strcmp(node1->name, "PropertyName") == 0)
{
pszValue = xmlNodeGetContent(node1);
if (pszLayerPropertyName == NULL)
pszLayerPropertyName = msStrdup(pszValue);
else
{
pszLayerPropertyName=
msStringConcatenate(pszLayerPropertyName, ",");
pszLayerPropertyName =
msStringConcatenate(pszLayerPropertyName, pszValue);
}
}
if (strcmp(node1->name, "Filter") == 0)
{
pszValue = xmlNodeGetContent(node1);
if (pszValue)
{
xmlBufferPtr buffer;
pszTmp = NULL;
buffer = xmlBufferCreate();
xmlNodeDump(buffer, node1->doc, node1, 0, 0);
pszTmp = msStringConcatenate(pszTmp, "(");
pszTmp = msStringConcatenate(pszTmp, buffer->content);
pszTmp = msStringConcatenate(pszTmp, ")");
xmlBufferFree(buffer);
wfsparams->pszFilter =
msStringConcatenate(wfsparams->pszFilter,pszTmp);
msFree(pszTmp);
}
}
}
pszTmp = NULL;
if (pszLayerPropertyName == NULL)
pszTmp = msStrdup("(!)");
else
{
pszTmp = msStringConcatenate(pszTmp, "(");
pszTmp = msStringConcatenate(pszTmp, pszLayerPropertyName);
pszTmp = msStringConcatenate(pszTmp, ")");
msFree(pszLayerPropertyName);
}
wfsparams->pszPropertyName = msStringConcatenate(wfsparams->pszPropertyName, pszTmp);
msFree(pszTmp);
}/*Query node*/
}
}
/* check for request */
/* only works??? if
psXPathTmp = msLibXml2GetXPath(doc, context, (xmlChar *)"/wfs:GetCapabilities|/GetCapabilities");
psXPathTmp = msLibXml2GetXPath(doc, context, (xmlChar *)"/GetCapabilities");
psXPathTmp = msLibXml2GetXPath(doc, context, (xmlChar *)"/wfs:GetCapabilities");
psXPathTmp = msLibXml2GetXPath(doc, context, (xmlChar *)"GetCapabilities");
psXPathTmp = msLibXml2GetXPath(doc, context, (xmlChar *)"//GetCapabilities");
*/
#else /*#ifdef WFS_USE_LIBXML2*/
CPLXMLNode *psRoot, *psQuery, *psFilter, *psTypeName, *psPropertyName = NULL;
CPLXMLNode *psGetFeature = NULL;
CPLXMLNode *psGetCapabilities = NULL;
CPLXMLNode *psDescribeFeature = NULL;
CPLXMLNode *psOperation = NULL;
const char *pszValue;
char *pszSerializedFilter, *pszTmp, *pszTmp2 = NULL;
int bMultiLayer = 0;
psRoot = CPLParseXMLString(request->postrequest);
if(map->debug == MS_DEBUGLEVEL_VVV)
{
msDebug("msWFSParseRequest(): WFS post request: %s\n", request->postrequest);
}
if (psRoot)
{
/* need to strip namespaces */
CPLStripXMLNamespace(psRoot, NULL, 1);
for( psOperation = psRoot;
psOperation != NULL;
psOperation = psOperation->psNext )
{
if(psOperation->eType == CXT_Element)
{
if(strcasecmp(psOperation->pszValue,"GetFeature")==0)
{
psGetFeature = psOperation;
break;
}
else if(strcasecmp(psOperation->pszValue,"GetCapabilities")==0)
{
psGetCapabilities = psOperation;
pszValue = CPLGetXMLValue(psGetFeature, "updateSequence", NULL);
if (pszValue)
wfsparams->pszUpdateSequence = msStrdup(pszValue);
break;
}
else if(strcasecmp(psOperation->pszValue,
"DescribeFeatureType")==0)
{
psDescribeFeature = psOperation;
break;
}
/* these are unsupported requests. Just set the */
/* request value and return; */
else if (strcasecmp(psOperation->pszValue,
"GetFeatureWithLock") == 0)
{
wfsparams->pszRequest = msStrdup("GetFeatureWithLock");
break;
}
else if (strcasecmp(psOperation->pszValue,
"LockFeature") == 0)
{
wfsparams->pszRequest = msStrdup("LockFeature");
break;
}
else if (strcasecmp(psOperation->pszValue,
"Transaction") == 0)
{
wfsparams->pszRequest = msStrdup("Transaction");
break;
}
}
}
/* -------------------------------------------------------------------- */
/* Parse the GetFeature */
/* -------------------------------------------------------------------- */
if (psGetFeature)
{
wfsparams->pszRequest = msStrdup("GetFeature");
pszValue = CPLGetXMLValue(psGetFeature, "version",
NULL);
if (pszValue)
wfsparams->pszVersion = msStrdup(pszValue);
pszValue = CPLGetXMLValue(psGetFeature, "service",
NULL);
if (pszValue)
wfsparams->pszService = msStrdup(pszValue);
pszValue = CPLGetXMLValue(psGetFeature, "resultType",
NULL);
if (pszValue)
wfsparams->pszResultType = msStrdup(pszValue);
pszValue = CPLGetXMLValue(psGetFeature, "maxFeatures",
NULL);
if (pszValue)
wfsparams->nMaxFeatures = atoi(pszValue);
pszValue = CPLGetXMLValue(psGetFeature, "startIndex",
NULL);
if (pszValue)
wfsparams->nStartIndex = atoi(pszValue);
pszValue = CPLGetXMLValue(psGetFeature, "outputFormat",
NULL);
if (pszValue)
wfsparams->pszOutputFormat = msStrdup(pszValue);
psQuery = CPLGetXMLNode(psGetFeature, "Query");
if (psQuery)
{
/* free typname and filter. There may have been */
/* values if they were passed in the URL */
if (wfsparams->pszTypeName)
free(wfsparams->pszTypeName);
wfsparams->pszTypeName = NULL;
if (wfsparams->pszFilter)
free(wfsparams->pszFilter);
wfsparams->pszFilter = NULL;
if (psQuery->psNext && psQuery->psNext->pszValue &&
strcasecmp(psQuery->psNext->pszValue, "Query") == 0)
bMultiLayer = 1;
/* -------------------------------------------------------------------- */
/* Parse typenames and filters. If there are multiple queries, */
/* typenames will be build with comma between thme */
/* (typename1,typename2,...) and filters will be build with */
/* bracets enclosinf the filters :(filter1)(filter2)... */
/* propertynames are stored like (property1,property2)(property1) */
/* -------------------------------------------------------------------- */
while (psQuery && psQuery->pszValue &&
strcasecmp(psQuery->pszValue, "Query") == 0)
{
/* get SRS */
pszValue = CPLGetXMLValue(psGetFeature, "srsName",
NULL);
if (pszValue)
wfsparams->pszSrs = msStrdup(pszValue);
/* parse typenames */
pszValue = CPLGetXMLValue(psQuery,
"typename", NULL);
if (pszValue)
{
if (wfsparams->pszTypeName == NULL)
wfsparams->pszTypeName = msStrdup(pszValue);
else
{
pszTmp = msStrdup(wfsparams->pszTypeName);
wfsparams->pszTypeName =
(char *)msSmallRealloc(wfsparams->pszTypeName,
sizeof(char)*
(strlen(pszTmp)+
strlen(pszValue)+2));
sprintf(wfsparams->pszTypeName,"%s,%s",pszTmp,
pszValue);
free(pszTmp);
}
}
/*parse property name*/
psPropertyName = CPLGetXMLNode(psQuery, "PropertyName");
pszTmp2= NULL;
/*when there is no PropertyName, we outpout (!) so that it is parsed properly in GetFeature*/
if (psPropertyName == NULL)
pszTmp2 = msStrdup("!");
while (psPropertyName)
{
if (!psPropertyName->pszValue ||
strcasecmp(psPropertyName->pszValue, "PropertyName") != 0)
{
psPropertyName = psPropertyName->psNext;
continue;
}
pszValue = CPLGetXMLValue(psPropertyName, NULL, NULL);
if (pszTmp2 == NULL) {
pszTmp2 = msStrdup(pszValue);
}
else
{
pszTmp = msStrdup(pszTmp2);
pszTmp2 = (char *)msSmallRealloc(pszTmp2, sizeof(char)* (strlen(pszTmp)+ strlen(pszValue)+2));
sprintf(pszTmp2,"%s,%s",pszTmp, pszValue);
msFree(pszTmp);
}
psPropertyName = psPropertyName->psNext;
}
if (pszTmp2)
{
pszTmp = msStrdup(pszTmp2);
pszTmp2 = (char *)msSmallRealloc(pszTmp2, sizeof(char)* (strlen(pszTmp)+ 3));
sprintf(pszTmp2,"(%s)", pszTmp);
msFree(pszTmp);
if (wfsparams->pszPropertyName == NULL)
wfsparams->pszPropertyName = msStrdup(pszTmp2);
else
{
pszTmp = msStrdup(wfsparams->pszPropertyName);
wfsparams->pszPropertyName =
(char *)msSmallRealloc(wfsparams->pszPropertyName,
sizeof(char)*(strlen(pszTmp)+ strlen(pszTmp2)+1));
sprintf(wfsparams->pszPropertyName,"%s%s",wfsparams->pszPropertyName,
pszTmp2);
msFree(pszTmp);
}
msFree(pszTmp2);
pszTmp2 = NULL;
}
/* parse filter */
psFilter = CPLGetXMLNode(psQuery, "Filter");
if (psFilter)
{
if (!bMultiLayer)
wfsparams->pszFilter = CPLSerializeXMLTree(psFilter);
else
{
pszSerializedFilter = CPLSerializeXMLTree(psFilter);
pszTmp = (char *)msSmallMalloc(sizeof(char)*
(strlen(pszSerializedFilter)+3));
sprintf(pszTmp, "(%s)", pszSerializedFilter);
free(pszSerializedFilter);
if (wfsparams->pszFilter == NULL)
wfsparams->pszFilter = msStrdup(pszTmp);
else
{
pszSerializedFilter = msStrdup(wfsparams->pszFilter);
wfsparams->pszFilter =
(char *)msSmallRealloc(wfsparams->pszFilter,
sizeof(char)*
(strlen(pszSerializedFilter)+
strlen(pszTmp)+1));
sprintf(wfsparams->pszFilter, "%s%s",
pszSerializedFilter, pszTmp);
free(pszSerializedFilter);
}
free(pszTmp);
}
}
psQuery = psQuery->psNext;
}/* while psQuery */
}
}/* end of GetFeatures */
/* -------------------------------------------------------------------- */
/* Parse GetCapabilities. */
/* -------------------------------------------------------------------- */
if (psGetCapabilities)
{
wfsparams->pszRequest = msStrdup("GetCapabilities");
pszValue = CPLGetXMLValue(psGetCapabilities, "version",
NULL);
/* version is optional for the GetCapabilities. If not */
/* provided, set it. */
if (pszValue)
wfsparams->pszVersion = msStrdup(pszValue);
else
wfsparams->pszVersion = msStrdup(msStrdup(msWFSGetDefaultVersion(map)));
pszValue =
CPLGetXMLValue(psGetCapabilities, "service",
NULL);
if (pszValue)
wfsparams->pszService = msStrdup(pszValue);
}/* end of GetCapabilites */
/* -------------------------------------------------------------------- */
/* Parse DescribeFeatureType */
/* -------------------------------------------------------------------- */
if (psDescribeFeature)
{
wfsparams->pszRequest = msStrdup("DescribeFeatureType");
pszValue = CPLGetXMLValue(psDescribeFeature, "version",
NULL);
if (pszValue)
wfsparams->pszVersion = msStrdup(pszValue);
pszValue = CPLGetXMLValue(psDescribeFeature, "service",
NULL);
if (pszValue)
wfsparams->pszService = msStrdup(pszValue);
pszValue = CPLGetXMLValue(psDescribeFeature,
"outputFormat",
NULL);
if (pszValue)
wfsparams->pszOutputFormat = msStrdup(pszValue);
psTypeName = CPLGetXMLNode(psDescribeFeature, "TypeName");
if (psTypeName)
{
/* free typname and filter. There may have been */
/* values if they were passed in the URL */
if (wfsparams->pszTypeName)
free(wfsparams->pszTypeName);
wfsparams->pszTypeName = NULL;
while (psTypeName && psTypeName->pszValue &&
strcasecmp(psTypeName->pszValue, "TypeName") == 0)
{
if (psTypeName->psChild && psTypeName->psChild->pszValue)
{
pszValue = psTypeName->psChild->pszValue;
if (wfsparams->pszTypeName == NULL)
wfsparams->pszTypeName = msStrdup(pszValue);
else
{
pszTmp = msStrdup(wfsparams->pszTypeName);
wfsparams->pszTypeName =
(char *)msSmallRealloc(wfsparams->pszTypeName,
sizeof(char)*
(strlen(pszTmp)+
strlen(pszValue)+2));
sprintf(wfsparams->pszTypeName,"%s,%s",pszTmp,
pszValue);
free(pszTmp);
}
}
psTypeName = psTypeName->psNext;
}
}
}/* end of DescibeFeatureType */
}
#endif /*USE_LIBXML2_WFS*/
}
#endif
#endif
return MS_SUCCESS;
}