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