/********************************************************************** * $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(" \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(" \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("\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; }