/********************************************************************** * $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" /* ** msWFSException() ** ** Report current MapServer error in XML exception format. */ int msWFSException(mapObj *map, const char *locator, const char *code, const char *version ) { char *schemalocation = NULL; /* In WFS, exceptions are always XML. */ if( version == NULL ) version = "1.0.0"; if( msOWSParseVersionString(version) >= OWS_1_1_0 ) return msWFSException11( map, code, locator, version ); msIO_printf("Content-type: text/xml%c%c",10,10); 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"); /* clear error since we have already reported it */ msResetErrorList(); return MS_FAILURE; /* so we can call 'return msWFSException();' anywhere */ } /* ** */ static void msWFSPrintRequestCap(const char *wmtver, const char *request, const char *script_url, const char *format_tag, const char *formats, ...) { va_list argp; const char *fmt; msIO_printf(" <%s>\n", request); /* We expect to receive a NULL-terminated args list of formats */ if (format_tag != NULL) { msIO_printf(" <%s>\n", format_tag); va_start(argp, formats); fmt = formats; while(fmt != NULL) { msIO_printf(" <%s/>\n", fmt); fmt = va_arg(argp, const char *); } va_end(argp); 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); } /* 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. ** lp->dump must be explicitly set to TRUE in mapfile. */ if (lp->dump && (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(&(map->projection),&(map->web.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), (char *)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) { char *script_url=NULL, *script_url_encoded; const char *updatesequence=NULL; const char *wmtver=NULL; char tmpString[OWS_VERSION_MAXLEN]; int wfsSupportedVersions[] = {OWS_1_1_0, OWS_1_0_0}; int wfsNumSupportedVersions = 2; int i=0, tmpInt=0; /* negotiate version */ tmpInt = msOWSNegotiateVersion(msOWSParseVersionString(wfsparams->pszVersion), wfsSupportedVersions, wfsNumSupportedVersions); /* set result as string and carry on */ if (wfsparams->pszVersion) msFree(wfsparams->pszVersion); wfsparams->pszVersion = strdup(msOWSGetVersionString(tmpInt, tmpString)); if( wfsparams->pszVersion == NULL || strncmp(wfsparams->pszVersion,"1.1",3) == 0 ) return msWFSGetCapabilities11( map, wfsparams, req ); /* Decide which version we're going to return... only 1.0.0 for now */ wmtver = strdup("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); } updatesequence = msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence"); if (!updatesequence) updatesequence = strdup("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); 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); return msWFSException(map, "updatesequence", "InvalidUpdateSequence", wmtver); } } msIO_printf("Content-type: text/xml%c%c",10,10); 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 */ msWFSPrintRequestCap(wmtver, "DescribeFeatureType", script_url_encoded, "SchemaDescriptionLanguage", "XMLSCHEMA", NULL); msWFSPrintRequestCap(wmtver, "GetFeature", script_url_encoded, "ResultFormat", "GML2", NULL); 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; /* List only vector layers in which DUMP=TRUE */ 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); 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)+3); if(!element_tab) return; 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) { 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 */ 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; ipszVersion == NULL || strncmp(paramsObj->pszVersion,"1.1",3) == 0 ) outputformat = OWS_SFE_SCHEMA; if (paramsObj->pszOutputFormat) { if(strcasecmp(paramsObj->pszOutputFormat, "XMLSCHEMA") == 0 || strstr(paramsObj->pszOutputFormat, "gml/2")!= NULL) outputformat = OWS_DEFAULT_SCHEMA; else if(strcasecmp(paramsObj->pszOutputFormat, "SFE_XMLSCHEMA") == 0 || strstr(paramsObj->pszOutputFormat, "gml/3")!= NULL) outputformat = OWS_SFE_SCHEMA; else { msSetError(MS_WFSERR, "Unsupported DescribeFeatureType outputFormat (%s).", "msWFSDescribeFeatureType()", paramsObj->pszOutputFormat); return msWFSException(map, "outputformat", "InvalidParameterValue", paramsObj->pszVersion); } } /* Validate layers */ if (numlayers > 0) { for (i=0; ipszTypeName); */ return msWFSException(map, "typename", "InvalidParameterValue", paramsObj->pszVersion); } } } /* ** retrieve any necessary external namespace/schema configuration information */ namespaceList = msGMLGetNamespaces(&(map->web), "G"); /* ** DescribeFeatureType response */ msIO_printf("Content-type: text/xml%c%c",10,10); 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)) { /* ** 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, "G"); 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() */ int msWFSGetFeature(mapObj *map, wfsParamsObj *paramsObj, cgiRequestObj *req) /* const char *wmtver, char **names, char **values, int numentries) */ { int i, maxfeatures=-1; const char *typename=""; char *script_url=NULL, *script_url_encoded; rectObj bbox; const char *pszOutputSRS = NULL; char **layers = NULL; int numlayers = 0; char *pszFilter = NULL; int bFilterSet = 0; int bBBOXSet = 0; int bFeatureIdSet = 0; char *pszNameSpace = NULL; 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, *encoded_typename, *encoded_schema; const char *tmpmaxfeatures = NULL; const char *output_schema_format = "XMLSCHEMA"; int outputformat = OWS_GML2; /* default output is GML 2.1 */ char **aFIDLayers = NULL; char **aFIDValues = NULL; int iFIDLayers = 0; gmlNamespaceListObj *namespaceList=NULL; /* for external application schema support */ /* Default filter is map extents */ bbox = map->extent; /* Read CGI parameters */ /* */ /* __TODO__ Need to support XML encoded requests */ /* */ /* 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; const char *pszMapSRS = NULL; char **tokens; int n=0, i=0; /* keep a ref for layer use. */ 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(typename, ',', &numlayers); /* ==================================================================== */ /* TODO: check if the typename contains namespaces (ex cdf:Other), */ /* If that is the case extarct 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) { /* pszNameSpace = strdup(tokens[0]); */ msFreeCharArray(tokens, n); for (i=0; iprojection), &(map->web.metadata), "FO", MS_TRUE); /* Keep only selected layers, set to OFF by default. */ for(j=0; jnumlayers; 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; lp = GET_LAYER(map, j); if (msWFSIsLayerSupported(lp) && lp->name && strcasecmp(lp->name, layers[k]) == 0) { const char *pszThisLayerSRS; char szBuf[32]; rectObj ext; bLayerFound = MS_TRUE; lp->status = MS_ON; if (lp->template == NULL) { /* Force setting a template to enable query. */ lp->template = strdup("ttt.html"); } /* See comments in msWFSGetCapabilities() about the rules for SRS. */ if ((pszThisLayerSRS = pszMapSRS) == NULL) { pszThisLayerSRS = msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE); } if (pszThisLayerSRS == NULL) { msSetError(MS_WFSERR, "Server config error: SRS must be set at least at the map or at the layer level.", "msWFSGetFeature()"); return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion); } /* Keep track of output SRS. If different from value */ /* from previous layers then this is an invalid request */ /* i.e. all layers in a single request must be in the */ /* same SRS. */ if (pszOutputSRS == NULL) { pszOutputSRS = pszThisLayerSRS; } else if (strcasecmp(pszThisLayerSRS,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()"); return msWFSException(map, "typename", "InvalidParameterValue", paramsObj->pszVersion); } /* set the map extent to the layer extent */ /* get the extent of the layer (bbox will get further filtered later */ /* if the client specifies BBOX or a spatial filter */ if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS && pszMapSRS != NULL) { sprintf(szBuf, "init=epsg:%.10s", pszMapSRS+5); if (szBuf != NULL) { if (msLoadProjectionString(&(map->projection), szBuf) != 0) { msSetError(MS_WFSERR, "msLoadProjectionString() failed: %s", "msWFSGetFeature()", szBuf); return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion); } } if (msProjectionsDiffer(&map->projection, &lp->projection) == MS_TRUE) { msProjectRect(&lp->projection, &map->projection, &(ext)); } bbox = map->extent = ext; } } } if (!bLayerFound) { /* Requested layer name was not in capabilities: * either it just doesn't exist, or it's missing DUMP TRUE */ 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); } } } /* Validate outputformat */ if (paramsObj->pszOutputFormat) { /* We support only GML2 and GML3 for now. */ if(strcasecmp(paramsObj->pszOutputFormat, "GML2") == 0) { outputformat = OWS_GML2; output_schema_format = "XMLSCHEMA"; } else if(strcasecmp(paramsObj->pszOutputFormat, "GML3") == 0) { outputformat = OWS_GML3; output_schema_format = "SFE_XMLSCHEMA"; } else { outputformat = OWS_GML2; output_schema_format = "XMLSCHEMA"; } } else { /*set the output format using the version if available*/ if(paramsObj->pszVersion == NULL || strncmp(paramsObj->pszVersion,"1.1",3) == 0 ) { outputformat = OWS_GML3; output_schema_format = "text/xml; subtype=gml/3.1.1"; } } /* 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; } /* if (strcasecmp(names[i], "FEATUREID") == 0) */ /* { */ /* */ /* } */ 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) { 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]); 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 (typename == NULL || strlen(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 == NULL || nFilters <=0 || nFilters != numlayers) { msSetError(MS_WFSERR, "Wrong number of FILTER attributes", "msWFSGetFeature()"); return msWFSException(map, "filter", "InvalidParameterValue", paramsObj->pszVersion); } paszFilter = (char **)malloc(sizeof(char *)*nFilters); for (i=0; ipszVersion); } psNode = FLTParseFilterEncoding(paszFilter[i]); 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_FALSE) != 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 **)malloc(sizeof(char *)*nTokens); aFIDValues = (char **)malloc(sizeof(char *)*nTokens); for (j=0; jpszVersion); } if (tokens1) msFreeCharArray(tokens1, nTokens1); } } if (tokens) msFreeCharArray(tokens, nTokens); 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 = strdup("ttt.html"); } psNode = FLTCreateFeatureIdFilterEncoding(aFIDValues[j]); if( FLTApplyFilterToLayer(psNode, map, lp->index, MS_FALSE) != 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; jprojection), szBuf) != 0) { msSetError(MS_WFSERR, "msLoadProjectionString() failed", "msWFSGetFeature()"); return msWFSException(map, "mapserv", "NoApplicableCode", 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) { if(msQueryByRect(map, -1, bbox) != 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); } } } /* ** GetFeature response */ if ((script_url=msOWSGetOnlineResource(map,"FO","onlineresource",req)) ==NULL || (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) { msSetError(MS_WFSERR, "Server URL not found", "msWFSGetFeature()"); return msWFSException(map, "mapserv", "NoApplicableCode", paramsObj->pszVersion); } /* ** retrieve any necessary external namespace/schema configuration information */ namespaceList = msGMLGetNamespaces(&(map->web), "G"); msIO_printf("Content-type: text/xml%c%c",10,10); 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); pszNameSpace = strdup(user_namespace_prefix); value = msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection"); if(value) collection_name = value; encoded = msEncodeHTMLEntities( paramsObj->pszVersion ); encoded_typename = msEncodeHTMLEntities( typename ); encoded_schema = msEncodeHTMLEntities( msOWSGetSchemasLocation(map) ); if(outputformat == OWS_GML2) { /* use a wfs:FeatureCollection */ 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(" 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, user_namespace_uri_encoded, script_url_encoded, encoded, encoded_typename, output_schema_format); } else { if(paramsObj->pszVersion && strncmp(paramsObj->pszVersion,"1.1",3) == 0 ) { 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); } } if(paramsObj->pszVersion && strncmp(paramsObj->pszVersion,"1.1",3) == 0 ) 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", user_namespace_uri_encoded, script_url_encoded, encoded, encoded_typename, output_schema_format); else msIO_printf(" xsi:schemaLocation=\"%s %sSERVICE=WFS&VERSION=%s&REQUEST=DescribeFeatureType&TYPENAME=%s&OUTPUTFORMAT=%s\">\n", user_namespace_uri_encoded, script_url_encoded, encoded, encoded_typename, output_schema_format); } msFree(encoded); msFree(encoded_schema); msFree(encoded_typename); /* handle case of maxfeatures = 0 */ if(maxfeatures != 0) msGMLWriteWFSQuery(map, stdout, maxfeatures, pszNameSpace, outputformat); /* if no results where written (TODO: this needs to be GML2/3 specific I imagine */ for(i=0; inumlayers; i++) { if (GET_LAYER(map, i)->resultcache && GET_LAYER(map, i)->resultcache->numresults > 0) break; } if ((i==map->numlayers) || (maxfeatures == 0)) { msIO_printf(" \n"); 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", user_namespace_prefix, collection_name); else msIO_printf("\n\n", user_namespace_prefix, collection_name); } /* ** Done! Now a bit of clean-up. */ free(script_url); free(script_url_encoded); if (pszNameSpace) free(pszNameSpace); msFree(user_namespace_uri_encoded); msGMLFreeNamespaces(namespaceList); return MS_SUCCESS; } #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) { #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. */ msWFSParseRequest(requestobj, paramsObj); /* 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) { 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) { 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) { 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 ) { returnvalue = msWFSGetCapabilities(map, paramsObj, requestobj); 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; } returnvalue = MS_DONE; /* Continue dispatching... */ if (strcasecmp(paramsObj->pszRequest, "DescribeFeatureType") == 0) returnvalue = msWFSDescribeFeatureType(map, paramsObj); else if (strcasecmp(paramsObj->pszRequest, "GetFeature") == 0) returnvalue = msWFSGetFeature(map, paramsObj, requestobj); 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)); if (paramsObj) { paramsObj->nMaxFeatures = -1; } return paramsObj; } /************************************************************************/ /* msWFSFreeParmsObj */ /* */ /* Free params object. */ /************************************************************************/ void msWFSFreeParamsObj(wfsParamsObj *wfsparams) { if (wfsparams) { if (wfsparams->pszVersion) free(wfsparams->pszVersion); if (wfsparams->pszUpdateSequence) free(wfsparams->pszUpdateSequence); if (wfsparams->pszRequest) free(wfsparams->pszRequest); if (wfsparams->pszService) free(wfsparams->pszService); if (wfsparams->pszTypeName) free(wfsparams->pszTypeName); if (wfsparams->pszFilter) free(wfsparams->pszFilter); if (wfsparams->pszFeatureId) free(wfsparams->pszFeatureId); if (wfsparams->pszOutputFormat) free(wfsparams->pszOutputFormat); } } /************************************************************************/ /* msWFSParseRequest */ /* */ /* Parse request into the params object. */ /************************************************************************/ void msWFSParseRequest(cgiRequestObj *request, wfsParamsObj *wfsparams) { #ifdef USE_WFS_SVR int i = 0; if (!request || !wfsparams) return; 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 = strdup(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "UPDATESEQUENCE") == 0) wfsparams->pszUpdateSequence = strdup(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "REQUEST") == 0) wfsparams->pszRequest = strdup(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "SERVICE") == 0) wfsparams->pszService = strdup(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "MAXFEATURES") == 0) wfsparams->nMaxFeatures = atoi(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "BBOX") == 0) wfsparams->pszBbox = strdup(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "TYPENAME") == 0) wfsparams->pszTypeName = strdup(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "FILTER") == 0) wfsparams->pszFilter = strdup(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "OUTPUTFORMAT") == 0) wfsparams->pszOutputFormat = strdup(request->ParamValues[i]); else if (strcasecmp(request->ParamNames[i], "FEATUREID") == 0) wfsparams->pszFeatureId = strdup(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 = strdup("1.1.0"); } /* -------------------------------------------------------------------- */ /* Parse the post request. It is assumed to be an XML document. */ /* -------------------------------------------------------------------- */ #ifdef USE_OGR if (request->postrequest) { CPLXMLNode *psRoot, *psQuery, *psFilter, *psTypeName = NULL; CPLXMLNode *psGetFeature = NULL; CPLXMLNode *psGetCapabilities = NULL; CPLXMLNode *psDescribeFeature = NULL; CPLXMLNode *psOperation = NULL; char *pszValue, *pszSerializedFilter, *pszTmp = NULL; int bMultiLayer = 0; psRoot = CPLParseXMLString(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 = (char*)CPLGetXMLValue(psGetFeature, "updateSequence", NULL); if (pszValue) wfsparams->pszUpdateSequence = strdup(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 = strdup("GetFeatureWithLock"); break; } else if (strcasecmp(psOperation->pszValue, "LockFeature") == 0) { wfsparams->pszRequest = strdup("LockFeature"); break; } else if (strcasecmp(psOperation->pszValue, "Transaction") == 0) { wfsparams->pszRequest = strdup("Transaction"); break; } } } /* -------------------------------------------------------------------- */ /* Parse the GetFeature */ /* -------------------------------------------------------------------- */ if (psGetFeature) { wfsparams->pszRequest = strdup("GetFeature"); pszValue = (char*)CPLGetXMLValue(psGetFeature, "version", NULL); if (pszValue) wfsparams->pszVersion = strdup(pszValue); pszValue = (char*)CPLGetXMLValue(psGetFeature, "service", NULL); if (pszValue) wfsparams->pszService = strdup(pszValue); pszValue = (char*)CPLGetXMLValue(psGetFeature, "maxFeatures", NULL); if (pszValue) wfsparams->nMaxFeatures = atoi(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)... */ /* -------------------------------------------------------------------- */ while (psQuery && psQuery->pszValue && strcasecmp(psQuery->pszValue, "Query") == 0) { /* parse typenames */ pszValue = (char*)CPLGetXMLValue(psQuery, "typename", NULL); if (pszValue) { if (wfsparams->pszTypeName == NULL) wfsparams->pszTypeName = strdup(pszValue); else { pszTmp = strdup(wfsparams->pszTypeName); wfsparams->pszTypeName = (char *)realloc(wfsparams->pszTypeName, sizeof(char)* (strlen(pszTmp)+ strlen(pszValue)+2)); sprintf(wfsparams->pszTypeName,"%s,%s",pszTmp, pszValue); free(pszTmp); } } /* parse filter */ psFilter = CPLGetXMLNode(psQuery, "Filter"); if (psFilter) { if (!bMultiLayer) wfsparams->pszFilter = CPLSerializeXMLTree(psFilter); else { pszSerializedFilter = CPLSerializeXMLTree(psFilter); pszTmp = (char *)malloc(sizeof(char)* (strlen(pszSerializedFilter)+3)); sprintf(pszTmp, "(%s)", pszSerializedFilter); free(pszSerializedFilter); if (wfsparams->pszFilter == NULL) wfsparams->pszFilter = strdup(pszTmp); else { pszSerializedFilter = strdup(wfsparams->pszFilter); wfsparams->pszFilter = (char *)realloc(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 = strdup("GetCapabilities"); pszValue = (char*)CPLGetXMLValue(psGetCapabilities, "version", NULL); /* version is optional is the GetCapabilities. If not */ /* provided, set it. */ if (pszValue) wfsparams->pszVersion = strdup(pszValue); else wfsparams->pszVersion = strdup("1.0.0"); pszValue = (char*)CPLGetXMLValue(psGetCapabilities, "service", NULL); if (pszValue) wfsparams->pszService = strdup(pszValue); }/* end of GetCapabilites */ /* -------------------------------------------------------------------- */ /* Parse DescribeFeatureType */ /* -------------------------------------------------------------------- */ if (psDescribeFeature) { wfsparams->pszRequest = strdup("DescribeFeatureType"); pszValue = (char*)CPLGetXMLValue(psDescribeFeature, "version", NULL); if (pszValue) wfsparams->pszVersion = strdup(pszValue); pszValue = (char*)CPLGetXMLValue(psDescribeFeature, "service", NULL); if (pszValue) wfsparams->pszService = strdup(pszValue); pszValue = (char*)CPLGetXMLValue(psDescribeFeature, "outputFormat", NULL); if (pszValue) wfsparams->pszOutputFormat = strdup(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 = strdup(pszValue); else { pszTmp = strdup(wfsparams->pszTypeName); wfsparams->pszTypeName = (char *)realloc(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 #endif }