1 /**********************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  OGC WFS 2.0.0 implementation. This file holds some WFS 2.0.0
6  *           specific functions but other parts are still implemented in mapwfs.c.
7  * Author:   Even Rouault
8  *
9  **********************************************************************
10  * Copyright (c) 2013, Even Rouault
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in
20  * all copies of this Software or works derived from this Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  ****************************************************************************/
29 
30 #include "mapserver.h"
31 #include "mapows.h"
32 
33 #if defined(USE_WFS_SVR) && defined(USE_LIBXML2)
34 #include "maplibxml2.h"
35 #include "mapowscommon.h"
36 #include "mapogcfilter.h"
37 
38 #define MS_OWS_11_NAMESPACE_PREFIX       MS_OWSCOMMON_OWS_NAMESPACE_PREFIX
39 #define MS_OWS_11_NAMESPACE_URI          MS_OWSCOMMON_OWS_110_NAMESPACE_URI
40 
41 #define URN_GET_FEATURE_BY_ID "urn:ogc:def:query:OGC-WFS::GetFeatureById"
42 
43 #define GET_FEATURE_BY_ID \
44 "<StoredQueryDescription id=\"" URN_GET_FEATURE_BY_ID "\">" \
45 "<Title>Get feature by identifier</Title>" \
46 "<Abstract>Returns the single feature whose value is equal to the specified value of the ID argument</Abstract>" \
47 "<Parameter name=\"ID\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" type=\"xs:string\"/>" \
48 "<QueryExpressionText isPrivate=\"true\" language=\"urn:ogc:def:queryLanguage:OGC-WFS::WFS_QueryExpression\" returnFeatureTypes=\"\">" \
49 "<Query xmlns:fes=\"http://www.opengis.net/fes/2.0\" typeNames=\"?\">" \
50 "<fes:Filter><fes:ResourceId rid=\"${ID}\"/></fes:Filter>" \
51 "</Query>" \
52 "</QueryExpressionText>" \
53 "</StoredQueryDescription>"
54 
55 /************************************************************************/
56 /*                          msWFSException20()                          */
57 /************************************************************************/
58 
msWFSException20(mapObj * map,const char * locator,const char * exceptionCode)59 int msWFSException20(mapObj *map, const char *locator,
60                      const char *exceptionCode)
61 {
62   int size = 0;
63   char *errorString     = NULL;
64 
65   xmlDocPtr  psDoc      = NULL;
66   xmlNodePtr psRootNode = NULL;
67   xmlNsPtr   psNsOws    = NULL;
68   xmlChar *buffer       = NULL;
69   const char* status    = NULL;
70 
71   psNsOws = xmlNewNs(NULL, BAD_CAST MS_OWS_11_NAMESPACE_URI, BAD_CAST MS_OWS_11_NAMESPACE_PREFIX);
72 
73   errorString = msGetErrorString("\n");
74 
75   psDoc = xmlNewDoc(BAD_CAST "1.0");
76 
77   psRootNode = msOWSCommonExceptionReport(psNsOws, OWS_1_1_0, msOWSGetSchemasLocation(map), "2.0.0", msOWSGetLanguage(map, "exception"), exceptionCode, locator, errorString);
78 
79   xmlDocSetRootElement(psDoc, psRootNode);
80 
81   xmlNewNs(psRootNode, BAD_CAST MS_OWS_11_NAMESPACE_URI, BAD_CAST MS_OWS_11_NAMESPACE_PREFIX);
82 
83   /* Table D.2 OGC 09-025r1 - For CITE compliance */
84   if( EQUAL(exceptionCode, MS_OWS_ERROR_OPERATION_NOT_SUPPORTED) ||
85       EQUAL(exceptionCode, MS_OWS_ERROR_OPTION_NOT_SUPPORTED) ) {
86     status = "400 Not Implemented";
87   }
88   else if( EQUAL(exceptionCode, MS_OWS_ERROR_MISSING_PARAMETER_VALUE) ||
89            EQUAL(exceptionCode, MS_OWS_ERROR_INVALID_PARAMETER_VALUE) ||
90            EQUAL(exceptionCode, MS_OWS_ERROR_VERSION_NEGOTIATION_FAILED) ||
91            EQUAL(exceptionCode, MS_OWS_ERROR_INVALID_UPDATE_SEQUENCE) ) {
92     status = "400 Bad request";
93   }
94   else if( EQUAL(exceptionCode, MS_WFS_ERROR_OPERATION_PROCESSING_FAILED) ) {
95     status = "403 Server processing failed";
96   }
97   else if( EQUAL(exceptionCode, MS_OWS_ERROR_NO_APPLICABLE_CODE) ) {
98     status = "400 Internal Server Error";
99   }
100 
101   if( status != NULL )
102   {
103     msIO_setHeader("Status", "%s", status);
104   }
105   msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
106   msIO_sendHeaders();
107 
108   xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, "UTF-8", 1);
109 
110   msIO_printf("%s", buffer);
111 
112   /*free buffer and the document */
113   free(errorString);
114   xmlFree(buffer);
115   xmlFreeDoc(psDoc);
116   xmlFreeNs(psNsOws);
117 
118   /* clear error since we have already reported it */
119   msResetErrorList();
120 
121   return MS_FAILURE;
122 }
123 
124 /************************************************************************/
125 /*                          msWFSIncludeSection                         */
126 /************************************************************************/
msWFSIncludeSection(wfsParamsObj * params,const char * pszSection)127 static int msWFSIncludeSection(wfsParamsObj *params, const char* pszSection)
128 {
129     int i;
130     if( params->pszSections == NULL )
131         return TRUE;
132     for(i = 0; params->pszSections[i] != '\0'; i++ )
133     {
134         if( strncasecmp( params->pszSections + i, "All", strlen("All")) == 0 )
135             return TRUE;
136         if( strncasecmp( params->pszSections + i, pszSection, strlen(pszSection)) == 0 )
137             return TRUE;
138     }
139     return FALSE;
140 }
141 
142 /************************************************************************/
143 /*                       msWFSAddGlobalSRSNameParam                     */
144 /************************************************************************/
145 
msWFSAddGlobalSRSNameParam(xmlNodePtr psMainNode,xmlNsPtr psNsOws,mapObj * map)146 static void msWFSAddGlobalSRSNameParam(xmlNodePtr psMainNode,
147                                        xmlNsPtr psNsOws,
148                                        mapObj* map)
149 {
150 }
151 
152 /************************************************************************/
153 /*                       msWFSConstraintDefaultValue                    */
154 /************************************************************************/
155 
156 static
msWFSConstraintDefaultValue(xmlNsPtr psNs,xmlNsPtr psNsOws,const char * name,const char * value)157 xmlNodePtr msWFSConstraintDefaultValue(xmlNsPtr psNs, xmlNsPtr psNsOws, const char *name, const char* value)
158 {
159   xmlNodePtr psRootNode = NULL;
160 
161   psRootNode = xmlNewNode(psNs, BAD_CAST "Constraint");
162 
163   xmlNewProp(psRootNode, BAD_CAST "name", BAD_CAST name);
164 
165   xmlNewChild(psRootNode, psNsOws, BAD_CAST "NoValues", NULL );
166   xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "DefaultValue", BAD_CAST value);
167 
168   return psRootNode;
169 }
170 
171 /************************************************************************/
172 /*                              msWFSOperator                           */
173 /************************************************************************/
174 
175 static
msWFSOperator(xmlNsPtr psNsFES,const char * pszOperatorType,const char * pszOperator)176 xmlNodePtr msWFSOperator(xmlNsPtr psNsFES, const char* pszOperatorType, const char* pszOperator)
177 {
178     xmlNodePtr psNode = xmlNewNode(psNsFES, BAD_CAST pszOperatorType);
179     xmlNewProp(psNode, BAD_CAST "name", BAD_CAST pszOperator);
180     return psNode;
181 }
182 
183 /************************************************************************/
184 /*                       msWFS20FilterCapabilities                      */
185 /************************************************************************/
186 
187 static
msWFS20FilterCapabilities(xmlNsPtr psNsFES,xmlNsPtr psNsOws,int bImplementsSorting)188 xmlNodePtr msWFS20FilterCapabilities(xmlNsPtr psNsFES, xmlNsPtr psNsOws,
189                                      int bImplementsSorting)
190 {
191   xmlNodePtr psRootNode = NULL, psNode = NULL, psSubNode = NULL;
192 
193   psRootNode = xmlNewNode(psNsFES, BAD_CAST "Filter_Capabilities");
194 
195   psNode = xmlNewChild(psRootNode, psNsFES, BAD_CAST "Conformance", NULL);
196 
197     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsQuery", "TRUE"));
198     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsAdHocQuery", "TRUE"));
199     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsFunctions", "FALSE"));
200     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsResourceId", "TRUE"));
201     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsMinStandardFilter", "TRUE"));
202     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsStandardFilter", "TRUE"));
203     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsMinSpatialFilter", "TRUE"));
204     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsSpatialFilter", "FALSE"));
205     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsMinTemporalFilter", "TRUE"));
206     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsTemporalFilter", "FALSE"));
207     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsVersionNav", "FALSE"));
208     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsSorting", ( bImplementsSorting ) ? "TRUE" : "FALSE"));
209     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsExtendedOperators", "FALSE"));
210     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsMinimumXPath", "TRUE"));
211     xmlAddChild(psNode, msWFSConstraintDefaultValue(psNsFES, psNsOws, "ImplementsSchemaElementFunc", "FALSE"));
212 
213   psNode = xmlNewChild(psRootNode, psNsFES, BAD_CAST "Id_Capabilities", NULL);
214     psSubNode = xmlNewChild(psNode, psNsFES, BAD_CAST "ResourceIdentifier", NULL );
215     xmlNewProp(psSubNode, BAD_CAST "name", BAD_CAST "fes:ResourceId");
216 
217   psNode = xmlNewChild(psRootNode, psNsFES, BAD_CAST "Scalar_Capabilities", NULL);
218   xmlNewChild(psNode, psNsFES, BAD_CAST "LogicalOperators", NULL);
219   psNode = xmlNewChild(psNode, psNsFES, BAD_CAST "ComparisonOperators", NULL);
220   xmlAddChild(psNode, msWFSOperator(psNsFES, "ComparisonOperator", "PropertyIsEqualTo"));
221   xmlAddChild(psNode, msWFSOperator(psNsFES, "ComparisonOperator", "PropertyIsNotEqualTo"));
222   xmlAddChild(psNode, msWFSOperator(psNsFES, "ComparisonOperator", "PropertyIsLessThan"));
223   xmlAddChild(psNode, msWFSOperator(psNsFES, "ComparisonOperator", "PropertyIsGreaterThan"));
224   xmlAddChild(psNode, msWFSOperator(psNsFES, "ComparisonOperator", "PropertyIsLessThanOrEqualTo"));
225   xmlAddChild(psNode, msWFSOperator(psNsFES, "ComparisonOperator", "PropertyIsGreaterThanOrEqualTo"));
226   xmlAddChild(psNode, msWFSOperator(psNsFES, "ComparisonOperator", "PropertyIsLike"));
227   xmlAddChild(psNode, msWFSOperator(psNsFES, "ComparisonOperator", "PropertyIsBetween"));
228   /* Missing: PropertyIsNull, PropertyIsNil */
229 
230   psNode = xmlNewChild(psRootNode, psNsFES, BAD_CAST "Spatial_Capabilities", NULL);
231 
232   psSubNode = xmlNewChild(psNode, psNsFES, BAD_CAST "GeometryOperands", NULL);
233   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:Point"));
234   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:MultiPoint"));
235   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:LineString"));
236   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:MultiLineString"));
237   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:Curve"));
238   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:MultiCurve"));
239   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:Polygon"));
240   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:MultiPolygon"));
241   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:Surface"));
242   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:MultiSurface"));
243   /* xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:MultiGeometry")); */
244   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:Box"));
245   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "GeometryOperand", "gml:Envelope"));
246 
247   psSubNode = xmlNewChild(psNode, psNsFES, BAD_CAST "SpatialOperators", NULL);
248 #ifdef USE_GEOS
249   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Equals"));
250   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Disjoint"));
251   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Touches"));
252   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Within"));
253   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Overlaps"));
254   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Crosses"));
255   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Intersects"));
256   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Contains"));
257   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "DWithin"));
258   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "Beyond"));
259 #endif
260   xmlAddChild(psSubNode, msWFSOperator(psNsFES, "SpatialOperator", "BBOX"));
261 
262   psNode = xmlNewChild(psRootNode, psNsFES, BAD_CAST "Temporal_Capabilities", NULL);
263     psSubNode = xmlNewChild(psNode, psNsFES, BAD_CAST "TemporalOperands", NULL);
264     xmlAddChild(psSubNode, msWFSOperator(psNsFES, "TemporalOperand", "gml:TimePeriod"));
265     xmlAddChild(psSubNode, msWFSOperator(psNsFES, "TemporalOperand", "gml:TimeInstant"));
266 
267     psSubNode = xmlNewChild(psNode, psNsFES, BAD_CAST "TemporalOperators", NULL);
268     xmlAddChild(psSubNode, msWFSOperator(psNsFES, "TemporalOperator", "During"));
269 
270   return psRootNode;
271 }
272 
273 /************************************************************************/
274 /*                          msXMLStripIndentation                       */
275 /************************************************************************/
276 
msXMLStripIndentation(char * ptr)277 static void msXMLStripIndentation(char* ptr)
278 {
279     /* Remove spaces between > and < to get properly indented result */
280     char* afterLastClosingBracket = NULL;
281     if( *ptr == ' ' )
282         afterLastClosingBracket = ptr;
283     while( *ptr != '\0' )
284     {
285         if( *ptr == '<' && afterLastClosingBracket != NULL )
286         {
287             memmove(afterLastClosingBracket, ptr, strlen(ptr) + 1);
288             ptr = afterLastClosingBracket;
289         }
290         else if( *ptr == '>' )
291         {
292             afterLastClosingBracket = ptr + 1;
293         }
294         else if( *ptr != ' ' &&  *ptr != '\n' )
295             afterLastClosingBracket = NULL;
296         ptr ++;
297     }
298 }
299 
300 /************************************************************************/
301 /*                          msWFSAddInspireDSID                         */
302 /************************************************************************/
303 
msWFSAddInspireDSID(mapObj * map,xmlNsPtr psNsInspireDls,xmlNsPtr psNsInspireCommon,xmlNodePtr pDlsExtendedCapabilities)304 static void msWFSAddInspireDSID(mapObj *map,
305                                 xmlNsPtr psNsInspireDls,
306                                 xmlNsPtr psNsInspireCommon,
307                                 xmlNodePtr pDlsExtendedCapabilities)
308 {
309     const char* dsid_code = msOWSLookupMetadata(&(map->web.metadata), "FO", "inspire_dsid_code");
310     const char* dsid_ns = msOWSLookupMetadata(&(map->web.metadata), "FO", "inspire_dsid_ns");
311     if( dsid_code == NULL )
312     {
313         xmlAddChild(pDlsExtendedCapabilities, xmlNewComment(BAD_CAST "WARNING: Required metadata \"inspire_dsid_code\" missing"));
314     }
315     else
316     {
317         int ntokensCode = 0, ntokensNS = 0;
318         char** tokensCode;
319         char** tokensNS = NULL;
320         int i;
321 
322         tokensCode = msStringSplit(dsid_code, ',', &ntokensCode);
323         if( dsid_ns != NULL )
324             tokensNS = msStringSplitComplex( dsid_ns, ",", &ntokensNS, MS_ALLOWEMPTYTOKENS);
325         if( ntokensNS > 0 && ntokensNS != ntokensCode )
326         {
327             xmlAddChild(pDlsExtendedCapabilities,
328                         xmlNewComment(BAD_CAST "WARNING: \"inspire_dsid_code\" and \"inspire_dsid_ns\" have not the same number of elements. Ignoring inspire_dsid_ns"));
329             msFreeCharArray(tokensNS, ntokensNS);
330             tokensNS = NULL;
331             ntokensNS = 0;
332         }
333         for(i = 0; i<ntokensCode; i++ )
334         {
335             xmlNodePtr pSDSI = xmlNewNode(psNsInspireDls, BAD_CAST "SpatialDataSetIdentifier");
336             xmlAddChild(pDlsExtendedCapabilities, pSDSI);
337             xmlNewTextChild(pSDSI, psNsInspireCommon, BAD_CAST "Code", BAD_CAST tokensCode[i]);
338             if( ntokensNS > 0 && tokensNS[i][0] != '\0' )
339                 xmlNewTextChild(pSDSI, psNsInspireCommon, BAD_CAST "Namespace", BAD_CAST tokensNS[i]);
340         }
341         msFreeCharArray(tokensCode, ntokensCode);
342         if( ntokensNS > 0 )
343             msFreeCharArray(tokensNS, ntokensNS);
344     }
345 }
346 
347 
348 
349 /************************************************************************/
350 /*                          msWFSGetCapabilities20                      */
351 /*                                                                      */
352 /*      Return the capabilities document for WFS 2.0.0                  */
353 /************************************************************************/
msWFSGetCapabilities20(mapObj * map,wfsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)354 int msWFSGetCapabilities20(mapObj *map, wfsParamsObj *params,
355                            cgiRequestObj *req, owsRequestObj *ows_request)
356 {
357   xmlDocPtr psDoc = NULL;       /* document pointer */
358   xmlNodePtr psRootNode, psMainNode, psNode, psFtNode = NULL;
359   const char *updatesequence=NULL;
360   xmlNsPtr psNsOws, psNsXLink;
361   xmlNsPtr psNsFES = NULL;
362   xmlNsPtr psNsInspireCommon = NULL;
363   xmlNsPtr psNsInspireDls = NULL;
364   xmlDocPtr pInspireTmpDoc = NULL;
365   char *xsi_schemaLocation = NULL;
366   const char *user_namespace_prefix = NULL;
367   const char *user_namespace_uri = NULL;
368   gmlNamespaceListObj *namespaceList=NULL; /* for external application schema support */
369 
370   char *script_url=NULL, *formats_list;
371   const char *value = NULL;
372 
373   xmlChar *buffer = NULL;
374   int size = 0, i;
375   msIOContext *context = NULL;
376 
377   int ows_version = OWS_1_1_0;
378   int ret;
379 
380   char* validated_language;
381   int bImplementsSorting = MS_FALSE;
382 
383   /* -------------------------------------------------------------------- */
384   /*      Handle updatesequence                                           */
385   /* -------------------------------------------------------------------- */
386   ret = msWFSHandleUpdateSequence(map, params, "msWFSGetCapabilities20()");
387   if( ret != MS_SUCCESS )
388       return ret;
389 
390   validated_language = msOWSGetLanguageFromList(map, "FO", params->pszLanguage);
391 
392   /* -------------------------------------------------------------------- */
393   /*      Create document.                                                */
394   /* -------------------------------------------------------------------- */
395   psDoc = xmlNewDoc(BAD_CAST "1.0");
396 
397   psRootNode = xmlNewNode(NULL, BAD_CAST "WFS_Capabilities");
398 
399   xmlDocSetRootElement(psDoc, psRootNode);
400 
401   /* -------------------------------------------------------------------- */
402   /*      Name spaces                                                     */
403   /* -------------------------------------------------------------------- */
404 
405   /*default name space*/
406   xmlNewProp(psRootNode, BAD_CAST "xmlns", BAD_CAST MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
407 
408   xmlSetNs(psRootNode, xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_GML_32_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_GML_NAMESPACE_PREFIX));
409   xmlSetNs(psRootNode, xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_WFS_20_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_WFS_NAMESPACE_PREFIX));
410 
411   psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_110_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
412   psNsXLink = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XLINK_NAMESPACE_PREFIX);
413   xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
414   xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_FES_20_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_FES_20_NAMESPACE_PREFIX );
415 
416   value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
417   if(value)
418       user_namespace_uri = value;
419   else
420       user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
421 
422   value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
423   if(value)
424       user_namespace_prefix = value;
425   else
426       user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
427 
428   if(user_namespace_prefix != NULL && msIsXMLTagValid(user_namespace_prefix) == MS_FALSE)
429     msIO_printf("<!-- WARNING: The value '%s' is not valid XML namespace. -->\n", user_namespace_prefix);
430   else
431     xmlNewNs(psRootNode, BAD_CAST user_namespace_uri, BAD_CAST user_namespace_prefix);
432 
433   /* any additional namespaces */
434   namespaceList = msGMLGetNamespaces(&(map->web), "G");
435   for(i=0; i<namespaceList->numnamespaces; i++) {
436     if(namespaceList->namespaces[i].uri) {
437       xmlNewNs(psRootNode, BAD_CAST namespaceList->namespaces[i].uri, BAD_CAST namespaceList->namespaces[i].prefix);
438     }
439   }
440   msGMLFreeNamespaces(namespaceList);
441 
442   if ( msOWSLookupMetadata(&(map->web.metadata), "FO", "inspire_capabilities") ) {
443       psNsInspireCommon = xmlNewNs(psRootNode, BAD_CAST MS_INSPIRE_COMMON_NAMESPACE_URI, BAD_CAST MS_INSPIRE_COMMON_NAMESPACE_PREFIX);
444       psNsInspireDls = xmlNewNs(psRootNode, BAD_CAST MS_INSPIRE_DLS_NAMESPACE_URI, BAD_CAST MS_INSPIRE_DLS_NAMESPACE_PREFIX);
445   }
446 
447   xmlNewProp(psRootNode, BAD_CAST "version", BAD_CAST params->pszVersion );
448 
449   updatesequence = msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence");
450 
451   if (updatesequence)
452     xmlNewProp(psRootNode, BAD_CAST "updateSequence", BAD_CAST updatesequence);
453 
454   /*schema*/
455   xsi_schemaLocation = msStrdup(MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
456   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
457   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, msOWSGetSchemasLocation(map));
458   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION);
459 
460   if( psNsInspireDls != NULL )
461   {
462       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
463       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_INSPIRE_DLS_NAMESPACE_URI);
464       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
465       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, msOWSGetInspireSchemasLocation(map));
466       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_INSPIRE_DLS_SCHEMA_LOCATION);
467       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
468       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_INSPIRE_COMMON_NAMESPACE_URI);
469       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
470       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, msOWSGetInspireSchemasLocation(map));
471       xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_INSPIRE_COMMON_SCHEMA_LOCATION);
472   }
473 
474   xmlNewNsProp(psRootNode, NULL, BAD_CAST "xsi:schemaLocation", BAD_CAST xsi_schemaLocation);
475   free(xsi_schemaLocation);
476 
477   /* -------------------------------------------------------------------- */
478   /*      Service metadata.                                               */
479   /* -------------------------------------------------------------------- */
480 
481   /* TODO? : also add 1.1.0 and 1.0.0 as extra <ows:ServiceTypeVersion>. The OWS */
482   /* schema would suggest to do so, and also the example */
483   /* http://schemas.opengis.net/wfs/2.0/examples/GetCapabilities/GetCapabilities_Res_01.xml */
484   /* and Deegree too, but GeoServer also only lists the current version. */
485   if( msWFSIncludeSection(params, "ServiceIdentification") )
486   {
487     xmlAddChild(psRootNode,
488                 msOWSCommonServiceIdentification(psNsOws, map, "WFS",
489                                                  params->pszVersion, "FO", validated_language));
490   }
491 
492   /*service provider*/
493   if( msWFSIncludeSection(params, "ServiceProvider") )
494   {
495     xmlAddChild(psRootNode, msOWSCommonServiceProvider(
496                             psNsOws, psNsXLink, map, "FO", validated_language));
497   }
498 
499   /*operation metadata */
500   if( msWFSIncludeSection(params, "OperationsMetadata") )
501   {
502     if ((script_url=msOWSGetOnlineResource2(map, "FO", "onlineresource", req, validated_language)) == NULL) {
503         msSetError(MS_WFSERR, "Server URL not found", "msWFSGetCapabilities20()");
504         return msWFSException11(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, params->pszVersion);
505     }
506 
507 
508     /* -------------------------------------------------------------------- */
509     /*      Operations metadata.                                            */
510     /* -------------------------------------------------------------------- */
511     psMainNode= xmlAddChild(psRootNode,msOWSCommonOperationsMetadata(psNsOws));
512 
513     /* -------------------------------------------------------------------- */
514     /*      GetCapabilities                                                 */
515     /* -------------------------------------------------------------------- */
516     psNode = xmlAddChild(psMainNode,
517                         msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"GetCapabilities",
518                             OWS_METHOD_GETPOST, script_url));
519 
520     xmlAddChild(psMainNode, psNode);
521 
522     /*accept version*/
523     xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws,
524                 "Parameter", "AcceptVersions",
525                 "2.0.0,1.1.0,1.0.0"));
526     /*format*/
527     xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws,
528                 "Parameter", "AcceptFormats",
529                 "text/xml"));
530     /*sections*/
531     xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws,
532                 "Parameter", "Sections",
533                 "ServiceIdentification,ServiceProvider,OperationsMetadata,FeatureTypeList,Filter_Capabilities"));
534 
535     /* -------------------------------------------------------------------- */
536     /*      DescribeFeatureType                                             */
537     /* -------------------------------------------------------------------- */
538     if (msOWSRequestIsEnabled(map, NULL, "F", "DescribeFeatureType", MS_TRUE)) {
539         psNode = xmlAddChild(psMainNode,
540                             msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"DescribeFeatureType",
541                                 OWS_METHOD_GETPOST, script_url));
542         xmlAddChild(psMainNode, psNode);
543 
544         /*output format*/
545         xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws,
546                     "Parameter", "outputFormat",
547                     "application/gml+xml; version=3.2,"
548                     "text/xml; subtype=gml/3.2.1,"
549                     "text/xml; subtype=gml/3.1.1,"
550                     "text/xml; subtype=gml/2.1.2"));
551     }
552 
553     /* -------------------------------------------------------------------- */
554     /*      GetFeature                                                      */
555     /* -------------------------------------------------------------------- */
556     if (msOWSRequestIsEnabled(map, NULL, "F", "GetFeature", MS_TRUE)) {
557 
558         psNode = xmlAddChild(psMainNode,
559                             msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"GetFeature",
560                                 OWS_METHOD_GETPOST, script_url));
561         xmlAddChild(psMainNode, psNode);
562 
563         formats_list = msWFSGetOutputFormatList( map, NULL, OWS_2_0_0 );
564         xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws,
565                     "Parameter", "outputFormat",
566                     formats_list));
567         msFree( formats_list );
568     }
569 
570     /* -------------------------------------------------------------------- */
571     /*      GetPropertyValue                                                */
572     /* -------------------------------------------------------------------- */
573     if (msOWSRequestIsEnabled(map, NULL, "F", "GetPropertyValue", MS_TRUE)) {
574 
575         psNode = xmlAddChild(psMainNode,
576                             msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"GetPropertyValue",
577                                 OWS_METHOD_GETPOST, script_url));
578         xmlAddChild(psMainNode, psNode);
579 
580         /* Only advertize built-in GML formats for GetPropertyValue. Not sure */
581         /* it makes sense to advertize OGR formats. */
582         xmlAddChild(psNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws,
583                     "Parameter", "outputFormat",
584                     "application/gml+xml; version=3.2,"
585                     "text/xml; subtype=gml/3.2.1,"
586                     "text/xml; subtype=gml/3.1.1,"
587                     "text/xml; subtype=gml/2.1.2"));
588     }
589 
590     /* -------------------------------------------------------------------- */
591     /*      ListStoredQueries                                               */
592     /* -------------------------------------------------------------------- */
593     if (msOWSRequestIsEnabled(map, NULL, "F", "ListStoredQueries", MS_TRUE)) {
594 
595         psNode = xmlAddChild(psMainNode,
596                             msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"ListStoredQueries",
597                                 OWS_METHOD_GETPOST, script_url));
598         xmlAddChild(psMainNode, psNode);
599     }
600 
601     /* -------------------------------------------------------------------- */
602     /*      DescribeStoredQueries                                           */
603     /* -------------------------------------------------------------------- */
604     if (msOWSRequestIsEnabled(map, NULL, "F", "DescribeStoredQueries", MS_TRUE)) {
605 
606         psNode = xmlAddChild(psMainNode,
607                             msOWSCommonOperationsMetadataOperation(psNsOws,psNsXLink,"DescribeStoredQueries",
608                                 OWS_METHOD_GETPOST, script_url));
609         xmlAddChild(psMainNode, psNode);
610     }
611 
612     xmlAddChild(psMainNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws,
613                 "Parameter", "version",
614                 "2.0.0,1.1.0,1.0.0"));
615 
616     msWFSAddGlobalSRSNameParam(psMainNode, psNsOws, map);
617 
618     /* Conformance declaration */
619     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsBasicWFS", "TRUE"));
620     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsTransactionalWFS", "FALSE"));
621     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsLockingWFS", "FALSE"));
622     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "KVPEncoding", "TRUE"));
623     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "XMLEncoding", "TRUE"));
624     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "SOAPEncoding", "FALSE"));
625     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsInheritance", "FALSE"));
626     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsRemoteResolve", "FALSE"));
627     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsResultPaging", "TRUE"));
628     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsStandardJoins", "FALSE"));
629     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsSpatialJoins", "FALSE"));
630     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsTemporalJoins", "FALSE"));
631     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ImplementsFeatureVersioning", "FALSE"));
632     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "ManageStoredQueries", "FALSE"));
633 
634     /* Capacity declaration */
635     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "PagingIsTransactionSafe", "FALSE"));
636 
637     value = msOWSLookupMetadata(&(map->web.metadata), "FO", "maxfeatures");
638     if (value) {
639     xmlAddChild(psMainNode, msWFSConstraintDefaultValue(psNsOws, psNsOws, "CountDefault", value));
640     }
641 
642     xmlAddChild(psMainNode, msOWSCommonOperationsMetadataDomainType(ows_version, psNsOws,
643                 "Constraint", "QueryExpressions","wfs:Query,wfs:StoredQuery"));
644 
645     /* Add Inspire Download Services extended capabilities */
646     if( psNsInspireDls != NULL )
647     {
648         msIOContext* old_context;
649         msIOContext* new_context;
650         msIOBuffer* buffer;
651         xmlNodePtr pRoot;
652         xmlNodePtr pOWSExtendedCapabilities;
653         xmlNodePtr pDlsExtendedCapabilities;
654         xmlNodePtr pChild;
655 
656         old_context = msIO_pushStdoutToBufferAndGetOldContext();
657         msOWSPrintInspireCommonExtendedCapabilities(stdout, map, "FO", OWS_WARN,
658                                                     "foo",
659                                                     "xmlns:" MS_INSPIRE_COMMON_NAMESPACE_PREFIX "=\"" MS_INSPIRE_COMMON_NAMESPACE_URI "\" "
660                                                     "xmlns:" MS_INSPIRE_DLS_NAMESPACE_PREFIX "=\"" MS_INSPIRE_DLS_NAMESPACE_URI "\" "
661                                                     "xmlns:xsi=\"" MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI "\"", validated_language, OWS_WFS);
662 
663         new_context = msIO_getHandler(stdout);
664         buffer = (msIOBuffer *) new_context->cbData;
665 
666         /* Remove spaces between > and < to get properly indented result */
667         msXMLStripIndentation( (char*) buffer->data );
668 
669         pInspireTmpDoc = xmlParseDoc((const xmlChar *)buffer->data);
670         pRoot = xmlDocGetRootElement(pInspireTmpDoc);
671         xmlReconciliateNs(psDoc, pRoot);
672 
673         pOWSExtendedCapabilities = xmlNewNode(psNsOws, BAD_CAST "ExtendedCapabilities");
674         xmlAddChild(psMainNode, pOWSExtendedCapabilities);
675 
676         pDlsExtendedCapabilities = xmlNewNode(psNsInspireDls, BAD_CAST "ExtendedCapabilities");
677         xmlAddChild(pOWSExtendedCapabilities, pDlsExtendedCapabilities);
678 
679         pChild = pRoot->children;
680         while(pChild != NULL)
681         {
682             xmlNodePtr pNext = pChild->next;
683             xmlUnlinkNode(pChild);
684             xmlAddChild(pDlsExtendedCapabilities, pChild);
685             pChild = pNext;
686         }
687 
688         msWFSAddInspireDSID(map, psNsInspireDls, psNsInspireCommon, pDlsExtendedCapabilities);
689 
690         msIO_restoreOldStdoutContext(old_context);
691     }
692   }
693 
694   /* -------------------------------------------------------------------- */
695   /*      FeatureTypeList                                                 */
696   /* -------------------------------------------------------------------- */
697   if( msWFSIncludeSection(params, "FeatureTypeList") )
698   {
699     psFtNode = xmlNewNode(NULL, BAD_CAST "FeatureTypeList");
700     xmlAddChild(psRootNode, psFtNode);
701   }
702 
703   for(i=0; i<map->numlayers; i++) {
704         layerObj *lp;
705         lp = GET_LAYER(map, i);
706 
707         if (!msIntegerInArray(lp->index, ows_request->enabled_layers, ows_request->numlayers))
708             continue;
709 
710         /* List only vector layers in which DUMP=TRUE */
711         if (msWFSIsLayerSupported(lp))
712         {
713           if( psFtNode != NULL ) {
714             xmlAddChild(psFtNode, msWFSDumpLayer11(map, lp, psNsOws, OWS_2_0_0, validated_language, script_url));
715           }
716 
717           /* As soon as at least one layer supports sorting, advertize sorting */
718           if( msLayerSupportsSorting(lp) )
719               bImplementsSorting = MS_TRUE;
720         }
721   }
722 
723   /* -------------------------------------------------------------------- */
724   /*      Filter capabilities.                                            */
725   /* -------------------------------------------------------------------- */
726   if( msWFSIncludeSection(params, "Filter_Capabilities") )
727   {
728     psNsFES = xmlNewNs(NULL, BAD_CAST MS_OWSCOMMON_FES_20_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_FES_20_NAMESPACE_PREFIX);
729     xmlAddChild(psRootNode, msWFS20FilterCapabilities(psNsFES, psNsOws, bImplementsSorting));
730   }
731 
732   /* -------------------------------------------------------------------- */
733   /*      Write out the document.                                         */
734   /* -------------------------------------------------------------------- */
735 
736   if( msIO_needBinaryStdout() == MS_FAILURE )
737     return MS_FAILURE;
738 
739   msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
740   msIO_sendHeaders();
741 
742   context = msIO_getHandler(stdout);
743 
744   xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, ("UTF-8"), 1);
745   msIO_contextWrite(context, buffer, size);
746   xmlFree(buffer);
747 
748   /*free buffer and the document */
749   /*xmlFree(buffer);*/
750   xmlFreeDoc(psDoc);
751   if( psNsFES != NULL )
752       xmlFreeNs(psNsFES);
753   if( pInspireTmpDoc != NULL )
754       xmlFreeDoc(pInspireTmpDoc);
755 
756   free(script_url);
757   msFree(validated_language);
758 
759   xmlCleanupParser();
760 
761   return(MS_SUCCESS);
762 }
763 
764 /************************************************************************/
765 /*                       msWFSGetStoredQueries                          */
766 /*                                                                      */
767 /* Result must be freed with msFreeCharArray()                          */
768 /************************************************************************/
769 
msWFSGetStoredQueries(mapObj * map,int * pn)770 static char** msWFSGetStoredQueries(mapObj *map, int* pn)
771 {
772   const char* value;
773   char** tokens;
774   int i,n;
775 
776   value = msOWSLookupMetadata(&(map->web.metadata), "F", "storedqueries");
777   if( value != NULL )
778   {
779     tokens = msStringSplit(value, ',', &n);
780     for(i=0;i<n;i++)
781     {
782         if( strcasecmp(tokens[i], URN_GET_FEATURE_BY_ID) == 0 )
783             break;
784     }
785     /* Add mandatory GetFeatureById stored query if not found */
786     if(i == n)
787     {
788         tokens = (char**) realloc(tokens, (n+1) * sizeof(char*));
789         memmove(tokens + 1, tokens, n * sizeof(char**));
790         tokens[0] = msStrdup(URN_GET_FEATURE_BY_ID);
791         n ++;
792     }
793   }
794   else
795   {
796       tokens = (char**) malloc(sizeof(char**));
797       tokens[0] = msStrdup(URN_GET_FEATURE_BY_ID);
798       n = 1;
799   }
800   *pn = n;
801   return tokens;
802 }
803 
804 /************************************************************************/
805 /*                       msWFSGetStoredQuery                            */
806 /*                                                                      */
807 /* Result must be freed with msFree()                                   */
808 /************************************************************************/
809 
msWFSGetStoredQuery(mapObj * map,const char * pszURN)810 static char* msWFSGetStoredQuery(mapObj *map, const char* pszURN)
811 {
812     const char* value;
813     char szKey[256];
814 
815     snprintf(szKey, sizeof(szKey), "%s_inlinedef", pszURN);
816     value = msOWSLookupMetadata(&(map->web.metadata), "F", szKey);
817     if( value != NULL )
818         return msStrdup(value);
819 
820     snprintf(szKey, sizeof(szKey), "%s_filedef", pszURN);
821     value = msOWSLookupMetadata(&(map->web.metadata), "F", szKey);
822     if( value != NULL )
823     {
824         FILE* f = fopen(value, "rb");
825         if( f != NULL )
826         {
827             char* pszBuffer;
828             int nread;
829             long length;
830 
831             fseek(f, 0, SEEK_END);
832             length = ftell(f);
833             if( length > 1000000 )
834             {
835                 msSetError(MS_WFSERR, "%s: too big (%ld bytes > 1000000)",
836                            "msWFSGetStoredQuery()", value, length);
837                 fclose(f);
838             }
839             else
840             {
841                 fseek(f, 0, SEEK_SET);
842                 pszBuffer = (char*) malloc((int)length + 1);
843                 if( pszBuffer == NULL )
844                 {
845                     msSetError(MS_WFSERR, "Cannot allocate %d bytes to read %s",
846                                "msWFSGetStoredQuery()",
847                                (int)length + 1, value);
848                     fclose(f);
849                 }
850                 else
851                 {
852                     nread = (int)fread(pszBuffer, 1, length, f);
853                     fclose(f);
854                     if( nread == length )
855                     {
856                         pszBuffer[nread] = '\0';
857                         return pszBuffer;
858                     }
859                     msSetError(MS_WFSERR, "Could only read %d bytes / %d of %s",
860                                "msWFSGetStoredQuery()",
861                                nread, (int)length, value);
862                     msFree(pszBuffer);
863                 }
864             }
865         }
866         else
867         {
868             msSetError(MS_WFSERR, "Cannot open %s", "msWFSGetStoredQuery()", value);
869         }
870     }
871 
872     if( strcasecmp(pszURN, URN_GET_FEATURE_BY_ID) == 0 )
873         return msStrdup(GET_FEATURE_BY_ID);
874     return NULL;
875 }
876 
877 /************************************************************************/
878 /*                     msWFSGetResolvedStoredQuery20                    */
879 /*                                                                      */
880 /* Result must be freed with msFree()                                   */
881 /************************************************************************/
882 
msWFSGetResolvedStoredQuery20(mapObj * map,wfsParamsObj * wfsparams,const char * id,hashTableObj * hashTable)883 char* msWFSGetResolvedStoredQuery20(mapObj *map,
884                                     wfsParamsObj *wfsparams,
885                                     const char* id,
886                                     hashTableObj* hashTable)
887 {
888     char* storedQuery;
889     xmlDocPtr psStoredQueryDoc;
890     xmlNodePtr psStoredQueryRoot, pChild;
891 
892     storedQuery = msWFSGetStoredQuery(map, id);
893     if( storedQuery == NULL )
894     {
895         msSetError(MS_WFSERR, "Unknown stored query id: %s", "msWFSGetResolvedStoredQuery20()",
896                    id);
897         msWFSException(map, "storedqueryid", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, wfsparams->pszVersion);
898         return NULL;
899     }
900 
901     psStoredQueryDoc = xmlParseDoc((const xmlChar*) storedQuery);
902     if( psStoredQueryDoc == NULL )
903     {
904         msSetError(MS_WFSERR, "Definition for stored query '%s' is invalid", "msWFSGetResolvedStoredQuery20()", id);
905         msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, wfsparams->pszVersion);
906         msFree(storedQuery);
907         return NULL;
908     }
909 
910     psStoredQueryRoot = xmlDocGetRootElement(psStoredQueryDoc);
911 
912     /* Check that all parameters are provided */
913     pChild = psStoredQueryRoot->children;
914     while(pChild != NULL)
915     {
916         if( pChild->type == XML_ELEMENT_NODE &&
917             strcmp((const char*) pChild->name, "Parameter") == 0 )
918         {
919             xmlChar* parameterName = xmlGetProp(pChild, BAD_CAST "name");
920             if( parameterName != NULL )
921             {
922                 char szTmp[256];
923                 const char* value = msLookupHashTable(hashTable, (const char*)parameterName);
924                 if( value == NULL )
925                 {
926                     msSetError(MS_WFSERR, "Stored query '%s' requires parameter '%s'", "msWFSParseRequest()",
927                                id, (const char*)parameterName);
928                     msWFSException(map, (const char*)parameterName, MS_OWS_ERROR_MISSING_PARAMETER_VALUE, wfsparams->pszVersion);
929                     msFree(storedQuery);
930                     xmlFree(parameterName);
931                     xmlFreeDoc(psStoredQueryDoc);
932                     return NULL;
933                 }
934 
935                 snprintf(szTmp, sizeof(szTmp), "${%s}", (const char*)parameterName);
936                 storedQuery = msReplaceSubstring(storedQuery, szTmp, value);
937             }
938             xmlFree(parameterName);
939         }
940         pChild = pChild->next;
941     }
942 
943     xmlFreeDoc(psStoredQueryDoc);
944 
945     return storedQuery;
946 }
947 
948 /************************************************************************/
949 /*                       msWFSListStoredQueries20                       */
950 /************************************************************************/
951 
msWFSListStoredQueries20(mapObj * map,wfsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)952 int msWFSListStoredQueries20(mapObj *map, wfsParamsObj *params,
953                              cgiRequestObj *req, owsRequestObj *ows_request)
954 {
955   xmlDocPtr psDoc;
956   xmlChar *buffer = NULL;
957   int size = 0;
958   msIOContext *context = NULL;
959   xmlNodePtr psRootNode;
960   char *xsi_schemaLocation = NULL;
961   int i, j;
962   int nStoredQueries = 0;
963   char** storedQueries = NULL;
964 
965   xmlDocPtr psStoredQueryDoc;
966   xmlNodePtr psStoredQueryRoot;
967 
968   /* -------------------------------------------------------------------- */
969   /*      Create document.                                                */
970   /* -------------------------------------------------------------------- */
971   psDoc = xmlNewDoc(BAD_CAST "1.0");
972 
973   psRootNode = xmlNewNode(NULL, BAD_CAST "ListStoredQueriesResponse");
974 
975   xmlDocSetRootElement(psDoc, psRootNode);
976 
977   /* -------------------------------------------------------------------- */
978   /*      Name spaces                                                     */
979   /* -------------------------------------------------------------------- */
980 
981   /*default name space*/
982   xmlNewProp(psRootNode, BAD_CAST "xmlns", BAD_CAST MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
983 
984   xmlSetNs(psRootNode, xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_WFS_20_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_WFS_NAMESPACE_PREFIX));
985 
986   xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
987 
988   /*schema*/
989   xsi_schemaLocation = msStrdup(MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
990   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
991   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, msOWSGetSchemasLocation(map));
992   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION);
993 
994   xmlNewNsProp(psRootNode, NULL, BAD_CAST "xsi:schemaLocation", BAD_CAST xsi_schemaLocation);
995   free(xsi_schemaLocation);
996 
997   /* -------------------------------------------------------------------- */
998   /*      Add queries                                                     */
999   /* -------------------------------------------------------------------- */
1000 
1001   storedQueries = msWFSGetStoredQueries(map, &nStoredQueries);
1002   for(i = 0; i < nStoredQueries; i++)
1003   {
1004     char* query = msWFSGetStoredQuery(map, storedQueries[i]);
1005     if( query != NULL )
1006     {
1007       xmlNodePtr pChild;
1008       xmlNodePtr psStoredQuery;
1009 
1010       psStoredQueryDoc = xmlParseDoc((const xmlChar*) query);
1011       if( psStoredQueryDoc == NULL )
1012       {
1013         char szMsg[256];
1014         msFree(query);
1015         snprintf(szMsg, sizeof(szMsg), "WARNING: Definition for stored query %s is invalid", storedQueries[i]);
1016         xmlAddChild(psRootNode, xmlNewComment(BAD_CAST szMsg));
1017         continue;
1018       }
1019 
1020       psStoredQueryRoot = xmlDocGetRootElement(psStoredQueryDoc);
1021 
1022       psStoredQuery = xmlNewNode(NULL, BAD_CAST "StoredQuery" );
1023       xmlNewProp(psStoredQuery, BAD_CAST "id", BAD_CAST storedQueries[i]);
1024       xmlAddChild(psRootNode, psStoredQuery);
1025 
1026       pChild = psStoredQueryRoot->children;
1027       while(pChild != NULL)
1028       {
1029         xmlNodePtr pNext = pChild->next;
1030         if( pChild->type == XML_ELEMENT_NODE &&
1031             strcmp((const char*) pChild->name, "Title") == 0 )
1032         {
1033             xmlUnlinkNode(pChild);
1034             xmlAddChild(psStoredQuery, pChild);
1035         }
1036         else if( pChild->type == XML_ELEMENT_NODE &&
1037             strcmp((const char*) pChild->name, "QueryExpressionText") == 0 )
1038         {
1039             xmlNodePtr psReturnFeatureType;
1040 
1041             if( strcasecmp( storedQueries[i], URN_GET_FEATURE_BY_ID ) == 0 )
1042             {
1043                 for(j=0; j<map->numlayers; j++) {
1044                     layerObj *lp;
1045                     const char *user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
1046                     const char *user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
1047                     const char *value;
1048                     char szValue[256];
1049 
1050                     lp = GET_LAYER(map, j);
1051 
1052                     if (!msIntegerInArray(lp->index, ows_request->enabled_layers, ows_request->numlayers) ||
1053                         !msWFSIsLayerSupported(lp))
1054                     continue;
1055 
1056 
1057                     value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
1058                     if(value) user_namespace_uri = value;
1059 
1060                     value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
1061                     if(value) user_namespace_prefix = value;
1062 
1063                     psReturnFeatureType = xmlNewNode(NULL, BAD_CAST "ReturnFeatureType" );
1064                     xmlNewNs(psReturnFeatureType, BAD_CAST user_namespace_uri, BAD_CAST user_namespace_prefix);
1065 
1066                     xmlAddChild(psStoredQuery, psReturnFeatureType);
1067                     snprintf(szValue, sizeof(szValue), "%s:%s", user_namespace_prefix, lp->name);
1068                     xmlAddChild(psReturnFeatureType, xmlNewText( BAD_CAST szValue ));
1069                 }
1070             }
1071             else
1072             {
1073                 xmlChar* returnFeatureTypes = xmlGetProp(pChild, BAD_CAST "returnFeatureTypes");
1074                 if( returnFeatureTypes != NULL && strlen((const char*)returnFeatureTypes) > 0 )
1075                 {
1076                     int ntypes;
1077                     char** types = msStringSplit((const char*)returnFeatureTypes, ' ', &ntypes);
1078                     for(j=0; j<ntypes; j++)
1079                     {
1080                         psReturnFeatureType = xmlNewNode(NULL, BAD_CAST "ReturnFeatureType" );
1081                         xmlAddChild(psStoredQuery, psReturnFeatureType);
1082                         xmlAddChild(psReturnFeatureType, xmlNewText( BAD_CAST types[j] ));
1083                     }
1084                     msFreeCharArray(types, ntypes);
1085                 }
1086                 else
1087                 {
1088                     psReturnFeatureType = xmlNewNode(NULL, BAD_CAST "ReturnFeatureType" );
1089                     xmlAddChild(psStoredQuery, psReturnFeatureType);
1090                     xmlAddChild(psReturnFeatureType, xmlNewComment( BAD_CAST "WARNING: Missing return type" ));
1091                 }
1092                 xmlFree(returnFeatureTypes);
1093             }
1094         }
1095         pChild = pNext;
1096       }
1097 
1098       xmlReconciliateNs(psDoc, psStoredQuery);
1099       xmlFreeDoc(psStoredQueryDoc);
1100       msFree(query);
1101     }
1102     else
1103     {
1104       char szMsg[256];
1105       snprintf(szMsg, sizeof(szMsg), "WARNING: Definition for stored query %s missing", storedQueries[i]);
1106       xmlAddChild(psRootNode, xmlNewComment(BAD_CAST szMsg));
1107     }
1108   }
1109   msFreeCharArray(storedQueries, nStoredQueries);
1110 
1111   /* -------------------------------------------------------------------- */
1112   /*      Write out the document.                                         */
1113   /* -------------------------------------------------------------------- */
1114 
1115   if( msIO_needBinaryStdout() == MS_FAILURE )
1116     return MS_FAILURE;
1117 
1118   msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
1119   msIO_sendHeaders();
1120 
1121   context = msIO_getHandler(stdout);
1122 
1123   xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, ("UTF-8"), 1);
1124   msIO_contextWrite(context, buffer, size);
1125   xmlFree(buffer);
1126 
1127   /*free buffer and the document */
1128   /*xmlFree(buffer);*/
1129   xmlFreeDoc(psDoc);
1130 
1131   xmlCleanupParser();
1132 
1133   return(MS_SUCCESS);
1134 }
1135 
1136 
1137 /************************************************************************/
1138 /*                     msWFSDescribeStoredQueries20                     */
1139 /************************************************************************/
1140 
msWFSDescribeStoredQueries20(mapObj * map,wfsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)1141 int msWFSDescribeStoredQueries20(mapObj *map, wfsParamsObj *params,
1142                              cgiRequestObj *req, owsRequestObj *ows_request)
1143 {
1144   xmlDocPtr psDoc;
1145   xmlChar *buffer = NULL;
1146   int size = 0;
1147   msIOContext *context = NULL;
1148   xmlNodePtr psRootNode;
1149   char *xsi_schemaLocation = NULL;
1150   int i, j;
1151   int nStoredQueries = 0;
1152   char** storedQueries = NULL;
1153 
1154   xmlDocPtr psStoredQueryDoc;
1155   xmlNodePtr psStoredQueryRoot;
1156 
1157   if( params->pszStoredQueryId != NULL ) {
1158     storedQueries = msStringSplit(params->pszStoredQueryId,',',&nStoredQueries);
1159     for(i = 0; i < nStoredQueries; i++)
1160     {
1161         char* query = msWFSGetStoredQuery(map, storedQueries[i]);
1162         if( query == NULL )
1163         {
1164             msSetError(MS_WFSERR, "Unknown stored query id: %s", "msWFSDescribeStoredQueries20()",
1165                        storedQueries[i]);
1166             msFreeCharArray(storedQueries, nStoredQueries);
1167             return msWFSException(map, "storedqueryid", MS_OWS_ERROR_INVALID_PARAMETER_VALUE, params->pszVersion);
1168         }
1169         msFree(query);
1170     }
1171   } else {
1172     storedQueries = msWFSGetStoredQueries(map, &nStoredQueries);
1173   }
1174 
1175   /* -------------------------------------------------------------------- */
1176   /*      Create document.                                                */
1177   /* -------------------------------------------------------------------- */
1178   psDoc = xmlNewDoc(BAD_CAST "1.0");
1179 
1180   psRootNode = xmlNewNode(NULL, BAD_CAST "DescribeStoredQueriesResponse");
1181 
1182   xmlDocSetRootElement(psDoc, psRootNode);
1183 
1184   /* -------------------------------------------------------------------- */
1185   /*      Name spaces                                                     */
1186   /* -------------------------------------------------------------------- */
1187 
1188   /*default name space*/
1189   xmlNewProp(psRootNode, BAD_CAST "xmlns", BAD_CAST MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
1190 
1191   xmlSetNs(psRootNode, xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_WFS_20_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_WFS_NAMESPACE_PREFIX));
1192 
1193   xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
1194 
1195   /*schema*/
1196   xsi_schemaLocation = msStrdup(MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
1197   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
1198   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, msOWSGetSchemasLocation(map));
1199   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION);
1200 
1201   xmlNewNsProp(psRootNode, NULL, BAD_CAST "xsi:schemaLocation", BAD_CAST xsi_schemaLocation);
1202   free(xsi_schemaLocation);
1203 
1204   /* -------------------------------------------------------------------- */
1205   /*      Add queries                                                     */
1206   /* -------------------------------------------------------------------- */
1207 
1208   for(i = 0; i < nStoredQueries; i++)
1209   {
1210     char* query = msWFSGetStoredQuery(map, storedQueries[i]);
1211     if( query != NULL )
1212     {
1213       xmlNodePtr pChild;
1214       xmlNs*  ns;
1215       xmlNodePtr psStoredQuery;
1216 
1217       psStoredQueryDoc = xmlParseDoc((const xmlChar*) query);
1218       if( psStoredQueryDoc == NULL )
1219       {
1220         char szMsg[256];
1221         msFree(query);
1222         snprintf(szMsg, sizeof(szMsg), "WARNING: Definition for stored query %s is invalid", storedQueries[i]);
1223         xmlAddChild(psRootNode, xmlNewComment(BAD_CAST szMsg));
1224         continue;
1225       }
1226 
1227       psStoredQueryRoot = xmlDocGetRootElement(psStoredQueryDoc);
1228 
1229       psStoredQuery = xmlNewNode(NULL, BAD_CAST "StoredQueryDescription" );
1230       xmlNewProp(psStoredQuery, BAD_CAST "id", BAD_CAST storedQueries[i]);
1231       xmlAddChild(psRootNode, psStoredQuery);
1232 
1233       ns = psStoredQueryRoot->nsDef;
1234       while( ns != NULL ) {
1235         xmlNewNs(psStoredQuery, BAD_CAST ns->href, BAD_CAST ns->prefix);
1236         ns = ns->next;
1237       }
1238 
1239       pChild = psStoredQueryRoot->children;
1240       while(pChild != NULL)
1241       {
1242         xmlNodePtr pNext = pChild->next;
1243 
1244         if( pChild->type == XML_ELEMENT_NODE &&
1245             strcmp((const char*) pChild->name, "QueryExpressionText") == 0 )
1246         {
1247             if( strcasecmp( storedQueries[i], URN_GET_FEATURE_BY_ID ) == 0 )
1248             {
1249                 char** arrayNsPrefix = (char**) malloc( sizeof(char*) * map->numlayers );
1250                 char** arrayNsUri = (char**) malloc( sizeof(char*) * map->numlayers );
1251                 int arraysize = 0;
1252                 int k;
1253                 char* returnFeatureTypes = NULL;
1254                 xmlNodePtr psQueryExpressionText;
1255 
1256                 psQueryExpressionText = xmlNewNode(NULL, BAD_CAST "QueryExpressionText" );
1257                 xmlAddChild(psStoredQuery, psQueryExpressionText);
1258                 xmlNewProp(psQueryExpressionText, BAD_CAST "isPrivate", BAD_CAST "true");
1259                 xmlNewProp(psQueryExpressionText, BAD_CAST "language", BAD_CAST "urn:ogc:def:queryLanguage:OGC-WFS::WFS_QueryExpression");
1260 
1261                 for(j=0; j<map->numlayers; j++) {
1262                     layerObj *lp;
1263                     const char *user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
1264                     const char *user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
1265                     const char *value;
1266                     char szValue[256];
1267 
1268                     lp = GET_LAYER(map, j);
1269 
1270                     if (!msIntegerInArray(lp->index, ows_request->enabled_layers, ows_request->numlayers) ||
1271                         !msWFSIsLayerSupported(lp))
1272                     continue;
1273 
1274                     value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
1275                     if(value) user_namespace_uri = value;
1276 
1277                     value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
1278                     if(value) user_namespace_prefix = value;
1279 
1280                     for(k=0;k<arraysize;k++)
1281                     {
1282                         if( strcmp(arrayNsPrefix[k], user_namespace_prefix) == 0 )
1283                             break;
1284                     }
1285                     if(k == arraysize) {
1286                       arrayNsPrefix[arraysize] = msStrdup(user_namespace_prefix);
1287                       arrayNsUri[arraysize] = msStrdup(user_namespace_uri);
1288                       arraysize ++;
1289 
1290                       xmlNewNs(psQueryExpressionText, BAD_CAST user_namespace_uri, BAD_CAST user_namespace_prefix);
1291                     }
1292 
1293                     if( returnFeatureTypes != NULL )
1294                       returnFeatureTypes = msStringConcatenate(returnFeatureTypes, " ");
1295                     snprintf(szValue, sizeof(szValue), "%s:%s", user_namespace_prefix, lp->name);
1296                     returnFeatureTypes = msStringConcatenate(returnFeatureTypes, szValue);
1297                 }
1298 
1299                 xmlNewProp(psQueryExpressionText, BAD_CAST "returnFeatureTypes", BAD_CAST returnFeatureTypes);
1300 
1301                 msFree(returnFeatureTypes);
1302                 msFreeCharArray(arrayNsPrefix, arraysize);
1303                 msFreeCharArray(arrayNsUri, arraysize);
1304             }
1305             else
1306             {
1307                 xmlChar* isPrivate = xmlGetProp(pChild, BAD_CAST "isPrivate");
1308                 if( isPrivate != NULL && strcmp((const char*)isPrivate, "true") == 0) {
1309                     xmlNodePtr pSubChild = xmlFirstElementChild(pChild);
1310                     xmlUnlinkNode(pSubChild);
1311                     xmlFreeNode(pSubChild);
1312                 }
1313                 xmlUnlinkNode(pChild);
1314                 xmlAddChild(psStoredQuery, pChild);
1315                 msFree(isPrivate);
1316             }
1317         }
1318         else {
1319             xmlUnlinkNode(pChild);
1320             xmlAddChild(psStoredQuery, pChild);
1321         }
1322         pChild = pNext;
1323       }
1324 
1325       xmlReconciliateNs(psDoc, psStoredQuery);
1326       xmlFreeDoc(psStoredQueryDoc);
1327       msFree(query);
1328     }
1329     else
1330     {
1331       char szMsg[256];
1332       snprintf(szMsg, sizeof(szMsg), "WARNING: Definition for stored query %s missing", storedQueries[i]);
1333       xmlAddChild(psRootNode, xmlNewComment(BAD_CAST szMsg));
1334     }
1335   }
1336   msFreeCharArray(storedQueries, nStoredQueries);
1337 
1338   /* -------------------------------------------------------------------- */
1339   /*      Write out the document.                                         */
1340   /* -------------------------------------------------------------------- */
1341 
1342   if( msIO_needBinaryStdout() == MS_FAILURE )
1343     return MS_FAILURE;
1344 
1345   msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
1346   msIO_sendHeaders();
1347 
1348   context = msIO_getHandler(stdout);
1349 
1350   xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, ("UTF-8"), 1);
1351   msIO_contextWrite(context, buffer, size);
1352   xmlFree(buffer);
1353 
1354   /*free buffer and the document */
1355   /*xmlFree(buffer);*/
1356   xmlFreeDoc(psDoc);
1357 
1358   xmlCleanupParser();
1359 
1360   return(MS_SUCCESS);
1361 }
1362 
1363 #endif /* defined(USE_WFS_SVR) && defined(USE_LIBXML2) */
1364 
1365 #if defined(USE_WFS_SVR) && !defined(USE_LIBXML2)
1366 
msWFSException20(mapObj * map,const char * locator,const char * exceptionCode)1367 int msWFSException20(mapObj *map, const char *locator,
1368                      const char *exceptionCode)
1369 {
1370   /* fallback to reporting using 1.0 style exceptions. */
1371   return msWFSException( map, locator, exceptionCode, "1.0.0" );
1372 }
1373 
msWFSGetCapabilities20(mapObj * map,wfsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)1374 int msWFSGetCapabilities20(mapObj *map, wfsParamsObj *params,
1375                            cgiRequestObj *req, owsRequestObj *ows_request)
1376 
1377 {
1378   msSetError( MS_WFSERR,
1379               "WFS 2.0 request made, but mapserver requires libxml2 for WFS 2.0 services and this is not configured.",
1380               "msWFSGetCapabilities20()" );
1381 
1382   return msWFSException11(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, params->pszVersion);
1383 }
1384 
msWFSListStoredQueries20(mapObj * map,wfsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)1385 int msWFSListStoredQueries20(mapObj *map, wfsParamsObj *params,
1386                              cgiRequestObj *req, owsRequestObj *ows_request)
1387 {
1388   msSetError( MS_WFSERR,
1389               "WFS 2.0 request made, but mapserver requires libxml2 for WFS 2.0 services and this is not configured.",
1390               "msWFSListStoredQueries20()");
1391 
1392   return msWFSException11(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, params->pszVersion);
1393 }
1394 
msWFSDescribeStoredQueries20(mapObj * map,wfsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)1395 int msWFSDescribeStoredQueries20(mapObj *map, wfsParamsObj *params,
1396                              cgiRequestObj *req, owsRequestObj *ows_request)
1397 {
1398   msSetError( MS_WFSERR,
1399               "WFS 2.0 request made, but mapserver requires libxml2 for WFS 2.0 services and this is not configured.",
1400               "msWFSDescribeStoredQueries20()" );
1401 
1402   return msWFSException11(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, params->pszVersion);
1403 }
1404 
msWFSGetResolvedStoredQuery20(mapObj * map,wfsParamsObj * params,const char * id,hashTableObj * hashTable)1405 char* msWFSGetResolvedStoredQuery20(mapObj *map,
1406                                     wfsParamsObj *params,
1407                                     const char* id,
1408                                     hashTableObj* hashTable)
1409 {
1410   msSetError( MS_WFSERR,
1411               "WFS 2.0 request made, but mapserver requires libxml2 for WFS 2.0 services and this is not configured.",
1412               "msWFSGetResolvedStoredQuery20()" );
1413 
1414   msWFSException11(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, params->pszVersion);
1415   return NULL;
1416 }
1417 
1418 #endif /* defined(USE_WFS_SVR) && !defined(USE_LIBXML2) */
1419