/********************************************************************** * $Id$ * * Project: MapServer * Purpose: OGC WFS 1.1.0 implementation. This file holds some WFS 1.1.0 * specific functions but other parts are still implemented in mapwfs.c. * Author: Y. Assefa, DM Solutions Group (assefa@dmsolutions.ca) * ********************************************************************** * Copyright (c) 2008, Y. Assefa, 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" #include "mapows.h" MS_CVSID("$Id$") #if defined(USE_WFS_SVR) && defined(USE_LIBXML2) #include "maplibxml2.h" #include "mapowscommon.h" #include "mapogcfilter.h" /************************************************************************/ /* msWFSException11() */ /************************************************************************/ int msWFSException11(mapObj *map, const char *locator, const char *exceptionCode, const char *version) { int size = 0; char *errorString = NULL; char *errorMessage = NULL; char *schemasLocation = NULL; const char *encoding; xmlDocPtr psDoc = NULL; xmlNodePtr psRootNode = NULL; xmlNsPtr psNsOws = NULL; xmlChar *buffer = NULL; if (version == NULL) version = "1.1.0"; psNsOws = xmlNewNs(NULL, BAD_CAST "http://www.opengis.net/ows", BAD_CAST "ows"); encoding = msOWSLookupMetadata(&(map->web.metadata), "FO", "encoding"); errorString = msGetErrorString("\n"); errorMessage = msEncodeHTMLEntities(errorString); schemasLocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map)); psDoc = xmlNewDoc(BAD_CAST "1.0"); psRootNode = msOWSCommonExceptionReport(psNsOws, OWS_1_0_0, schemasLocation, version, msOWSGetLanguage(map, "exception"), exceptionCode, locator, errorMessage); xmlDocSetRootElement(psDoc, psRootNode); xmlNewNs(psRootNode, BAD_CAST "http://www.opengis.net/ows", BAD_CAST "ows"); if (encoding) msIO_setHeader("Content-type","text/xml; charset=%s", encoding); else msIO_setHeader("Content-type","text/xml"); msIO_sendHeaders(); xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, (encoding ? encoding : "ISO-8859-1"), 1); msIO_printf("%s", buffer); /*free buffer and the document */ free(errorString); free(errorMessage); free(schemasLocation); xmlFree(buffer); xmlFreeDoc(psDoc); xmlFreeNs(psNsOws); /* clear error since we have already reported it */ msResetErrorList(); return MS_FAILURE; } /************************************************************************/ /* msWFSDumpLayer11 */ /************************************************************************/ static xmlNodePtr msWFSDumpLayer11(mapObj *map, layerObj *lp, xmlNsPtr psNsOws) { rectObj ext; xmlNodePtr psRootNode, psNode; const char *value = NULL; const char *encoding = NULL; char *encoded=NULL; char *valueToFree; char **tokens; int n=0,i=0; encoding = msOWSLookupMetadata(&(map->web.metadata), "FO", "encoding"); if (!encoding) encoding = "ISO-8859-1"; psRootNode = xmlNewNode(NULL, BAD_CAST "FeatureType"); /*if there is an encoding using it on some of the items*/ psNode = msOWSCommonxmlNewChildEncoded(psRootNode, NULL, "Name", lp->name, encoding); if (lp->name && strlen(lp->name) > 0 && (msIsXMLTagValid(lp->name) == MS_FALSE || isdigit(lp->name[0]))) xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: The layer name '%s' might contain spaces or " "invalid characters or may start with a number. This could lead to potential problems")); value = msOWSLookupMetadata(&(lp->metadata), "FO", "title"); if (!value) value =(const char*)lp->name; psNode = msOWSCommonxmlNewChildEncoded(psRootNode, NULL, "Title", value, encoding); value = msOWSLookupMetadata(&(lp->metadata), "FO", "abstract"); if (value) psNode = msOWSCommonxmlNewChildEncoded(psRootNode, NULL, "Abstract", value, encoding); value = msOWSLookupMetadata(&(lp->metadata), "FO", "keywordlist"); if (value) { if (encoding) encoded = msGetEncodedString(value, encoding); else encoded = msGetEncodedString(value, "ISO-8859-1"); msLibXml2GenerateList( xmlNewChild(psRootNode, psNsOws, BAD_CAST "Keywords", NULL), NULL, "Keyword", encoded, ',' ); msFree(encoded); } /*support DefaultSRS and OtherSRS*/ valueToFree = msOWSGetProjURN(&(map->projection),&(map->web.metadata),"FO",MS_FALSE); if (!valueToFree) valueToFree = msOWSGetProjURN(&(lp->projection), &(lp->metadata), "FO", MS_FALSE); if (valueToFree) { tokens = msStringSplit(valueToFree, ' ', &n); if (tokens && n > 0) { psNode = xmlNewChild(psRootNode, NULL, BAD_CAST "DefaultSRS", BAD_CAST tokens[0]); for (i=1; iprojection.numargs > 0) { if (!pj_is_latlong(&lp->projection.proj)) msProjectRect(&lp->projection, NULL, &ext); } else if (map->projection.numargs > 0 && !pj_is_latlong(&map->projection.proj)) msProjectRect(&map->projection, NULL, &ext); xmlAddChild(psRootNode, msOWSCommonWGS84BoundingBox( psNsOws, 2, ext.minx, ext.miny, ext.maxx, ext.maxy)); } else { xmlNewChild(psRootNode, psNsOws, BAD_CAST "WGS84BoundingBox", NULL); xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: Optional WGS84BoundingBox could not be established for this layer. Consider setting the EXTENT in the LAYER object, or wfs_extent metadata. Also check that your data exists in the DATA statement")); } value = msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_href"); if (value) { psNode = xmlNewChild(psRootNode, NULL, BAD_CAST "MetadataURL", BAD_CAST value); value = msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_format"); if (!value) value = msStrdup("text/html"); /* default */ xmlNewProp(psNode, BAD_CAST "format", BAD_CAST value); value = msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_type"); if (!value) value = msStrdup("FGDC"); /* default */ xmlNewProp(psNode, BAD_CAST "type", BAD_CAST value); } return psRootNode; } /************************************************************************/ /* msWFSGetCapabilities11 */ /* */ /* Return the capabilities document for wfs 1.1.0 */ /************************************************************************/ int msWFSGetCapabilities11(mapObj *map, wfsParamsObj *params, cgiRequestObj *req, owsRequestObj *ows_request) { xmlDocPtr psDoc = NULL; /* document pointer */ xmlNodePtr psRootNode, psMainNode, psNode, psFtNode; xmlNodePtr psTmpNode; const char *updatesequence=NULL; xmlNsPtr psNsOws, psNsXLink, psNsOgc; char *schemalocation = NULL; char *xsi_schemaLocation = NULL; char *script_url=NULL, *script_url_encoded=NULL, *formats_list; const char *value = NULL; const char *encoding; xmlChar *buffer = NULL; int size = 0, i; msIOContext *context = NULL; int ows_version = OWS_1_0_0; /* -------------------------------------------------------------------- */ /* Handle updatesequence */ /* -------------------------------------------------------------------- */ updatesequence = msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence"); encoding = msOWSLookupMetadata(&(map->web.metadata), "FO", "encoding"); if (params->pszUpdateSequence != NULL) { i = msOWSNegotiateUpdateSequence(params->pszUpdateSequence, updatesequence); if (i == 0) { /* current */ msSetError(MS_WFSERR, "UPDATESEQUENCE parameter (%s) is equal to server (%s)", "msWFSGetCapabilities11()", params->pszUpdateSequence, updatesequence); return msWFSException11(map, "updatesequence", "CurrentUpdateSequence", params->pszVersion); } if (i > 0) { /* invalid */ msSetError(MS_WFSERR, "UPDATESEQUENCE parameter (%s) is higher than server (%s)", "msWFSGetCapabilities11()", params->pszUpdateSequence, updatesequence); return msWFSException11(map, "updatesequence", "InvalidUpdateSequence", params->pszVersion); } } /* -------------------------------------------------------------------- */ /* Create document. */ /* -------------------------------------------------------------------- */ psDoc = xmlNewDoc(BAD_CAST "1.0"); psRootNode = xmlNewNode(NULL, BAD_CAST "WFS_Capabilities"); xmlDocSetRootElement(psDoc, psRootNode); /* -------------------------------------------------------------------- */ /* Name spaces */ /* -------------------------------------------------------------------- */ /*default name space*/ xmlNewProp(psRootNode, BAD_CAST "xmlns", BAD_CAST "http://www.opengis.net/wfs"); xmlSetNs(psRootNode, xmlNewNs(psRootNode, BAD_CAST "http://www.opengis.net/gml", BAD_CAST "gml")); xmlSetNs(psRootNode, xmlNewNs(psRootNode, BAD_CAST "http://www.opengis.net/wfs", BAD_CAST "wfs")); psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX); psNsXLink = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_PREFIX); xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX); xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_PREFIX ); xmlNewProp(psRootNode, BAD_CAST "version", BAD_CAST params->pszVersion ); updatesequence = msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence"); if (updatesequence) xmlNewProp(psRootNode, BAD_CAST "updateSequence", BAD_CAST updatesequence); /*schema*/ schemalocation = msEncodeHTMLEntities( msOWSGetSchemasLocation(map) ); xsi_schemaLocation = msStrdup("http://www.opengis.net/wfs"); xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " "); xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, schemalocation); xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, "/wfs/1.1.0/wfs.xsd"); xmlNewNsProp(psRootNode, NULL, BAD_CAST "xsi:schemaLocation", BAD_CAST xsi_schemaLocation); /* -------------------------------------------------------------------- */ /* Service metadata. */ /* -------------------------------------------------------------------- */ psTmpNode = xmlAddChild(psRootNode, msOWSCommonServiceIdentification(psNsOws, map, "OGC WFS", params->pszVersion, "FO")); /*service provider*/ psTmpNode = xmlAddChild(psRootNode, msOWSCommonServiceProvider( psNsOws, psNsXLink, map, "FO")); /*operation metadata */ if ((script_url=msOWSGetOnlineResource(map, "FO", "onlineresource", req)) == NULL || (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) { msSetError(MS_WFSERR, "Server URL not found", "msWFSGetCapabilities11()"); return msWFSException11(map, "mapserv", "NoApplicableCode", params->pszVersion); } /* -------------------------------------------------------------------- */ /* Operations metadata. */ /* -------------------------------------------------------------------- */ psMainNode= xmlAddChild(psRootNode,msOWSCommonOperationsMetadata(psNsOws)); /* -------------------------------------------------------------------- */ /* GetCapabilities */ /* -------------------------------------------------------------------- */ psNode = xmlAddChild(psMainNode, msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"GetCapabilities", OWS_METHOD_GETPOST, script_url_encoded)); xmlAddChild(psMainNode, psNode); xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType( ows_version, psNsOws, "Parameter", "service", "WFS")); /*accept version*/ xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws, "Parameter", "AcceptVersions", "1.0.0,1.1.0")); /*format*/ xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws, "Parameter", "AcceptFormats", "text/xml")); /* -------------------------------------------------------------------- */ /* DescribeFeatureType */ /* -------------------------------------------------------------------- */ if (msOWSRequestIsEnabled(map, NULL, "F", "DescribeFeatureType", MS_TRUE)) { psNode = xmlAddChild(psMainNode, msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"DescribeFeatureType", OWS_METHOD_GETPOST, script_url_encoded)); xmlAddChild(psMainNode, psNode); /*output format*/ xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws, "Parameter", "outputFormat", "XMLSCHEMA,text/xml; subtype=gml/2.1.2,text/xml; subtype=gml/3.1.1")); } /* -------------------------------------------------------------------- */ /* GetFeature */ /* -------------------------------------------------------------------- */ if (msOWSRequestIsEnabled(map, NULL, "F", "GetFeature", MS_TRUE)) { psNode = xmlAddChild(psMainNode, msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"GetFeature", OWS_METHOD_GETPOST, script_url_encoded)); xmlAddChild(psMainNode, psNode); xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws, "Parameter", "resultType", "results,hits")); formats_list = msWFSGetOutputFormatList( map, NULL, "1.1.0" ); xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws, "Parameter", "outputFormat", formats_list)); msFree( formats_list ); value = msOWSLookupMetadata(&(map->web.metadata), "FO", "maxfeatures"); if (value) { xmlAddChild(psMainNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws, "Constraint", "DefaultMaxFeatures", (char *)value)); } } /* -------------------------------------------------------------------- */ /* FeatureTypeList */ /* -------------------------------------------------------------------- */ psFtNode = xmlNewNode(NULL, BAD_CAST "FeatureTypeList"); xmlAddChild(psRootNode, psFtNode); psNode = xmlNewChild(psFtNode, NULL, BAD_CAST "Operations", NULL); xmlNewChild(psNode, NULL, BAD_CAST "Operation", BAD_CAST "Query"); for(i=0; inumlayers; i++) { layerObj *lp; lp = GET_LAYER(map, i); if (!msIntegerInArray(lp->index, ows_request->enabled_layers, ows_request->numlayers)) continue; /* List only vector layers in which DUMP=TRUE */ if (msWFSIsLayerSupported(lp)) xmlAddChild(psFtNode, msWFSDumpLayer11(map, lp, psNsOws)); } /* -------------------------------------------------------------------- */ /* Filter capabilities. */ /* -------------------------------------------------------------------- */ psNsOgc = xmlNewNs(NULL, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OGC_NAMESPACE_PREFIX); xmlAddChild(psRootNode, FLTGetCapabilities(psNsOgc, psNsOgc, MS_FALSE)); /* -------------------------------------------------------------------- */ /* Write out the document. */ /* -------------------------------------------------------------------- */ if( msIO_needBinaryStdout() == MS_FAILURE ) return MS_FAILURE; if (encoding) msIO_setHeader("Content-type","text/xml; charset=%s", encoding); else msIO_setHeader("Content-type","text/xml"); msIO_sendHeaders(); context = msIO_getHandler(stdout); xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, (encoding ? encoding : "ISO-8859-1"), 1); msIO_contextWrite(context, buffer, size); xmlFree(buffer); /*free buffer and the document */ /*xmlFree(buffer);*/ xmlFreeDoc(psDoc); xmlFreeNs(psNsOgc); free(script_url); free(script_url_encoded); free(xsi_schemaLocation); free(schemalocation); xmlCleanupParser(); return(MS_SUCCESS); } #endif /*defined(USE_WFS_SVR) && defined(USE_LIBXML2)*/ #if defined(USE_WFS_SVR) && !defined(USE_LIBXML2) int msWFSGetCapabilities11(mapObj *map, wfsParamsObj *params, cgiRequestObj *req, owsRequestObj *ows_request) { msSetError( MS_WFSERR, "WFS 1.1 request made, but mapserver requires libxml2 for WFS 1.1 services and this is not configured.", "msWFSGetCapabilities11()", "NoApplicableCode" ); return msWFSException11(map, "mapserv", "NoApplicableCode", params->pszVersion); } int msWFSException11(mapObj *map, const char *locator, const char *exceptionCode, const char *version) { /* fallback to reporting using 1.0 style exceptions. */ return msWFSException( map, locator, exceptionCode, "1.0.0" ); } #endif /* defined(USE_WFS_SVR) && !defined(USE_LIBXML2) */