1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  OGC OWS Common Implementation for use by MapServer OGC code
6  *           versions:
7  *           1.0.0 (OGC Document 05-008c1)
8  *           1.1.0 (OGC document 06-121r3)
9  *
10  * Author:   Tom Kralidis (tomkralidis@gmail.com)
11  *
12  ******************************************************************************
13  * Copyright (c) 2006, Tom Kralidis
14  *
15  * Permission is hereby granted, free of charge, to any person obtaining a
16  * copy of this software and associated documentation files (the "Software"),
17  * to deal in the Software without restriction, including without limitation
18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19  * and/or sell copies of the Software, and to permit persons to whom the
20  * Software is furnished to do so, subject to the following conditions:
21  *
22  * The above copyright notice and this permission notice shall be included in
23  * all copies of this Software or works derived from this Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  ****************************************************************************/
33 
34 #include "mapserver.h"
35 #include "mapows.h"
36 
37 #ifdef USE_LIBXML2
38 
39 #include<libxml/parser.h>
40 #include<libxml/tree.h>
41 
42 #include "mapowscommon.h"
43 #include "maplibxml2.h"
44 
45 
46 
47 
48 
49 /**
50  * msOWSCommonServiceIdentification()
51  *
52  * returns an object of ServiceIdentification as per:
53  *
54  * 1.0.0 subclause 7.4.3
55  * 1.1.1 subclause 7.4.4
56  *
57  * @param map mapObj used to fetch WEB/METADATA
58  * @param servicetype the OWS type
59  * @param supported_versions the supported version(s) of the OWS
60  *
61  * @return psRootNode xmlNodePtr of XML construct
62  *
63  */
64 
msOWSCommonServiceIdentification(xmlNsPtr psNsOws,mapObj * map,const char * servicetype,const char * supported_versions,const char * namespaces,const char * validated_language)65 xmlNodePtr msOWSCommonServiceIdentification(xmlNsPtr psNsOws, mapObj *map,
66                                             const char *servicetype,
67                                             const char *supported_versions,
68                                             const char *namespaces,
69                                             const char *validated_language)
70 {
71   const char *value    = NULL;
72 
73   xmlNodePtr   psRootNode = NULL;
74   xmlNodePtr   psNode     = NULL;
75 
76   if (_validateNamespace(psNsOws) == MS_FAILURE)
77     psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
78 
79   /* create element name */
80   psRootNode = xmlNewNode(psNsOws, BAD_CAST "ServiceIdentification");
81 
82   /* add child elements */
83 
84   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "title", validated_language);
85 
86   psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "Title", BAD_CAST value);
87 
88   if (!value) {
89     xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_title\" missing for ows:Title"));
90   }
91 
92   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "abstract", validated_language);
93 
94   psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "Abstract", BAD_CAST value);
95 
96   if (!value) {
97     xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_abstract\" was missing for ows:Abstract"));
98   }
99 
100   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "keywordlist", validated_language);
101 
102   if (value) {
103     psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "Keywords", NULL);
104     msLibXml2GenerateList(psNode, psNsOws, "Keyword", value, ',');
105   }
106 
107   else {
108     xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_keywordlist\" was missing for ows:KeywordList"));
109   }
110 
111   psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "ServiceType", BAD_CAST servicetype);
112 
113   xmlNewProp(psNode, BAD_CAST "codeSpace", BAD_CAST MS_OWSCOMMON_OGC_CODESPACE);
114 
115   msLibXml2GenerateList(psRootNode, psNsOws, "ServiceTypeVersion", supported_versions, ',');
116 
117   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "fees", validated_language);
118 
119   psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "Fees", BAD_CAST value);
120 
121   if (!value) {
122     xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_fees\" was missing for ows:Fees"));
123   }
124 
125   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "accessconstraints", validated_language);
126 
127   psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "AccessConstraints", BAD_CAST value);
128 
129   if (!value) {
130     xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_accessconstraints\" was missing for ows:AccessConstraints"));
131   }
132 
133   return psRootNode;
134 }
135 
136 /**
137  * msOWSCommonServiceProvider()
138  *
139  * returns an object of ServiceProvider as per:
140  *
141  *
142  * 1.0.0 subclause 7.4.4
143  * 1.1.0 subclause 7.4.5
144  *
145  * @param map mapObj to fetch MAP/WEB/METADATA
146  *
147  * @return psRootNode xmlNodePtr pointer of XML construct
148  *
149  */
150 
msOWSCommonServiceProvider(xmlNsPtr psNsOws,xmlNsPtr psNsXLink,mapObj * map,const char * namespaces,const char * validated_language)151 xmlNodePtr msOWSCommonServiceProvider(xmlNsPtr psNsOws, xmlNsPtr psNsXLink,
152                                       mapObj *map, const char *namespaces,
153                                       const char *validated_language)
154 {
155   const char *value = NULL;
156 
157   xmlNodePtr   psNode          = NULL;
158   xmlNodePtr   psRootNode      = NULL;
159   xmlNodePtr   psSubNode       = NULL;
160   xmlNodePtr   psSubSubNode    = NULL;
161   xmlNodePtr   psSubSubSubNode = NULL;
162 
163   if (_validateNamespace(psNsOws) == MS_FAILURE)
164     psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
165 
166   psRootNode = xmlNewNode(psNsOws, BAD_CAST "ServiceProvider");
167 
168   /* add child elements */
169 
170   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "contactorganization", validated_language);
171 
172   psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "ProviderName", BAD_CAST value);
173 
174   if (!value) {
175     xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: Mandatory metadata \"ows_contactorganization\" was missing for ows:ProviderName"));
176   }
177 
178   psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "ProviderSite", NULL);
179 
180   xmlNewNsProp(psNode, psNsXLink, BAD_CAST "type", BAD_CAST "simple");
181 
182   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "service_onlineresource", validated_language);
183 
184   xmlNewNsProp(psNode, psNsXLink, BAD_CAST "href", BAD_CAST value);
185 
186   if (!value) {
187     xmlAddSibling(psNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_service_onlineresource\" was missing for ows:ProviderSite/@xlink:href"));
188   }
189 
190   psNode = xmlNewTextChild(psRootNode, psNsOws, BAD_CAST "ServiceContact", NULL);
191 
192   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "contactperson", validated_language);
193 
194   psSubNode = xmlNewTextChild(psNode, psNsOws, BAD_CAST "IndividualName", BAD_CAST  value);
195 
196   if (!value) {
197     xmlAddSibling(psSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_contactperson\" was missing for ows:IndividualName"));
198   }
199 
200   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "contactposition", validated_language);
201 
202   psSubNode = xmlNewTextChild(psNode, psNsOws, BAD_CAST "PositionName", BAD_CAST value);
203 
204   if (!value) {
205     xmlAddSibling(psSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_contactposition\" was missing for ows:PositionName"));
206   }
207 
208   psSubNode = xmlNewTextChild(psNode, psNsOws, BAD_CAST "ContactInfo", NULL);
209 
210   psSubSubNode = xmlNewTextChild(psSubNode, psNsOws, BAD_CAST "Phone", NULL);
211 
212   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "contactvoicetelephone", validated_language);
213 
214   psSubSubSubNode = xmlNewTextChild(psSubSubNode, psNsOws, BAD_CAST "Voice", BAD_CAST value);
215 
216   if (!value) {
217     xmlAddSibling(psSubSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_contactvoicetelephone\" was missing for ows:Voice"));
218   }
219 
220   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "contactfacsimiletelephone", validated_language);
221 
222   psSubSubSubNode = xmlNewTextChild(psSubSubNode, psNsOws, BAD_CAST "Facsimile", BAD_CAST value);
223 
224   if (!value) {
225     xmlAddSibling(psSubSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_contactfacsimiletelephone\" was missing for ows:Facsimile"));
226   }
227 
228   psSubSubNode = xmlNewTextChild(psSubNode, psNsOws, BAD_CAST "Address", NULL);
229 
230   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "address", validated_language);
231 
232   psSubSubSubNode = xmlNewTextChild(psSubSubNode, psNsOws, BAD_CAST "DeliveryPoint", BAD_CAST value);
233 
234   if (!value) {
235     xmlAddSibling(psSubSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_address\" was missing for ows:DeliveryPoint"));
236   }
237 
238   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "city", validated_language);
239 
240   psSubSubSubNode = xmlNewTextChild(psSubSubNode, psNsOws, BAD_CAST "City", BAD_CAST value);
241 
242   if (!value) {
243     xmlAddSibling(psSubSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_city\" was missing for ows:City"));
244   }
245 
246   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "stateorprovince", validated_language);
247 
248   psSubSubSubNode = xmlNewTextChild(psSubSubNode, psNsOws, BAD_CAST "AdministrativeArea", BAD_CAST value);
249 
250   if (!value) {
251     xmlAddSibling(psSubSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_stateorprovince\" was missing for ows:AdministrativeArea"));
252   }
253 
254   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "postcode", validated_language);
255 
256   psSubSubSubNode = xmlNewTextChild(psSubSubNode, psNsOws, BAD_CAST "PostalCode", BAD_CAST value);
257 
258   if (!value) {
259     xmlAddSibling(psSubSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_postcode\" was missing for ows:PostalCode"));
260   }
261 
262   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "country", validated_language);
263 
264   psSubSubSubNode = xmlNewTextChild(psSubSubNode, psNsOws, BAD_CAST "Country", BAD_CAST value);
265 
266   if (!value) {
267     xmlAddSibling(psSubSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_country\" was missing for ows:Country"));
268   }
269 
270   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "contactelectronicmailaddress", validated_language);
271 
272   psSubSubSubNode = xmlNewTextChild(psSubSubNode, psNsOws, BAD_CAST "ElectronicMailAddress", BAD_CAST value);
273 
274   if (!value) {
275     xmlAddSibling(psSubSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_contactelectronicmailaddress\" was missing for ows:ElectronicMailAddress"));
276   }
277 
278   psSubSubNode = xmlNewTextChild(psSubNode, psNsOws, BAD_CAST "OnlineResource", NULL);
279 
280   xmlNewNsProp(psSubSubNode, psNsXLink, BAD_CAST "type", BAD_CAST "simple");
281 
282   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "service_onlineresource", validated_language);
283 
284   xmlNewNsProp(psSubSubNode, psNsXLink, BAD_CAST "href", BAD_CAST value);
285 
286   if (!value) {
287     xmlAddSibling(psSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_service_onlineresource\" was missing for ows:OnlineResource/@xlink:href"));
288   }
289 
290   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "hoursofservice", validated_language);
291 
292   psSubSubNode = xmlNewTextChild(psSubNode, psNsOws, BAD_CAST "HoursOfService", BAD_CAST value);
293 
294   if (!value) {
295     xmlAddSibling(psSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_hoursofservice\" was missing for ows:HoursOfService"));
296   }
297 
298   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "contactinstructions", validated_language);
299 
300   psSubSubNode = xmlNewTextChild(psSubNode, psNsOws, BAD_CAST "ContactInstructions", BAD_CAST value);
301 
302   if (!value) {
303     xmlAddSibling(psSubSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_contactinstructions\" was missing for ows:ContactInstructions"));
304   }
305 
306   value = msOWSLookupMetadataWithLanguage(&(map->web.metadata), namespaces, "role", validated_language);
307 
308   psSubNode = xmlNewTextChild(psNode, psNsOws, BAD_CAST "Role", BAD_CAST value);
309 
310   if (!value) {
311     xmlAddSibling(psSubNode, xmlNewComment(BAD_CAST "WARNING: Optional metadata \"ows_role\" was missing for ows:Role"));
312   }
313 
314   return psRootNode;
315 
316 }
317 
318 /**
319  * msOWSCommonOperationsMetadata()
320  *
321  * returns the root element of OperationsMetadata as per:
322  *
323  * 1.0.0 subclause 7.4.5
324  * 1.1.0 subclause 7.4.6
325  *
326  * @return psRootNode xmlNodePtr pointer of XML construct
327  *
328  */
329 
msOWSCommonOperationsMetadata(xmlNsPtr psNsOws)330 xmlNodePtr msOWSCommonOperationsMetadata(xmlNsPtr psNsOws)
331 {
332   xmlNodePtr psRootNode = NULL;
333 
334   if (_validateNamespace(psNsOws) == MS_FAILURE)
335     psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
336 
337   psRootNode = xmlNewNode(psNsOws, BAD_CAST "OperationsMetadata");
338   return psRootNode;
339 }
340 
341 /**
342  * msOWSCommonOperationsMetadataOperation()
343  *
344  * returns an Operation element of OperationsMetadata as per:
345  *
346  * 1.0.0 subclause 7.4.5
347  * 1.1.0 subclause 7.4.6
348  *
349  * @param name name of the Operation
350  * @param method HTTP method: OWS_METHOD_GET, OWS_METHOD_POST or OWS_METHOD_GETPOST)
351  * @param url online resource URL
352  *
353  * @return psRootNode xmlNodePtr pointer of XML construct
354  */
355 
msOWSCommonOperationsMetadataOperation(xmlNsPtr psNsOws,xmlNsPtr psXLinkNs,char * name,int method,char * url)356 xmlNodePtr msOWSCommonOperationsMetadataOperation(xmlNsPtr psNsOws, xmlNsPtr psXLinkNs, char *name, int method, char *url)
357 {
358   xmlNodePtr psRootNode      = NULL;
359   xmlNodePtr psNode          = NULL;
360   xmlNodePtr psSubNode       = NULL;
361   xmlNodePtr psSubSubNode    = NULL;
362 
363   if (_validateNamespace(psNsOws) == MS_FAILURE)
364     psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
365 
366 
367   psRootNode = xmlNewNode(psNsOws, BAD_CAST "Operation");
368 
369   xmlNewProp(psRootNode, BAD_CAST "name", BAD_CAST name);
370 
371   psNode = xmlNewChild(psRootNode, psNsOws, BAD_CAST "DCP", NULL);
372 
373   psSubNode = xmlNewChild(psNode, psNsOws, BAD_CAST "HTTP", NULL);
374 
375   if (method  == OWS_METHOD_GET || method == OWS_METHOD_GETPOST ) {
376     psSubSubNode = xmlNewChild(psSubNode, psNsOws, BAD_CAST "Get", NULL);
377     xmlNewNsProp(psSubSubNode, psXLinkNs, BAD_CAST "type", BAD_CAST "simple");
378     xmlNewNsProp(psSubSubNode, psXLinkNs, BAD_CAST "href", BAD_CAST url);
379   }
380 
381   if (method == OWS_METHOD_POST || method == OWS_METHOD_GETPOST ) {
382     psSubSubNode = xmlNewChild(psSubNode, psNsOws, BAD_CAST "Post", NULL);
383     xmlNewNsProp(psSubSubNode, psXLinkNs, BAD_CAST "type", BAD_CAST "simple");
384     xmlNewNsProp(psSubSubNode, psXLinkNs, BAD_CAST "href", BAD_CAST url);
385   }
386 
387   return psRootNode;
388 }
389 
390 /**
391  * msOWSCommonOperationsMetadataDomainType()
392  *
393  * returns a Parameter or Constraint element (which are of type ows:DomainType)
394  * of OperationsMetadata as per:
395  *
396  * 1.0.0 subclause 7.4.5
397  * 1.1.0 subclause 7.4.6
398  *
399  * @param version the integerized x.y.z version of OWS Common to use
400  * @param elname name of the element (Parameter | Constraint)
401  * @param name name of the Parameter
402  * @param values list of values (comma separated list) or NULL if none
403  *
404  * @return psRootNode xmlNodePtr pointer of XML construct
405  *
406  */
407 
msOWSCommonOperationsMetadataDomainType(int version,xmlNsPtr psNsOws,char * elname,char * name,char * values)408 xmlNodePtr msOWSCommonOperationsMetadataDomainType(int version, xmlNsPtr psNsOws, char *elname, char *name, char *values)
409 {
410   xmlNodePtr psRootNode = NULL;
411   xmlNodePtr psNode = NULL;
412 
413   if (_validateNamespace(psNsOws) == MS_FAILURE)
414     psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
415 
416   psRootNode = xmlNewNode(psNsOws, BAD_CAST elname);
417 
418   xmlNewProp(psRootNode, BAD_CAST "name", BAD_CAST name);
419 
420   if (version == OWS_1_0_0) {
421     msLibXml2GenerateList(psRootNode, psNsOws, "Value", values, ',');
422   }
423   if (version == OWS_1_1_0 || version == OWS_2_0_0) {
424     psNode = xmlNewChild(psRootNode, psNsOws, BAD_CAST "AllowedValues", NULL);
425     msLibXml2GenerateList(psNode, psNsOws, "Value", values, ',');
426   }
427 
428   return psRootNode;
429 }
430 
431 /**
432  * msOWSCommonExceptionReport()
433  *
434  * returns an object of ExceptionReport as per clause 8
435  *
436  * @param ows_version the version of OWS Common to use
437  * @param schemas_location URL to OGC Schemas Location base
438  * @param version the version of the calling specification
439  * @param language ISO3166 code of language
440  * @param exceptionCode a code from the calling specification's list of exceptions, or from OWS Common
441  * @param locator where the exception was encountered (i.e. "layers" keyword)
442  * @param ExceptionText the actual error message
443  *
444  * @return psRootNode xmlNodePtr pointer of XML construct
445  *
446  */
447 
msOWSCommonExceptionReport(xmlNsPtr psNsOws,int ows_version,const char * schemas_location,const char * version,const char * language,const char * exceptionCode,const char * locator,const char * ExceptionText)448 xmlNodePtr msOWSCommonExceptionReport(xmlNsPtr psNsOws, int ows_version, const char *schemas_location, const char *version, const char *language, const char *exceptionCode, const char *locator, const char *ExceptionText)
449 {
450   char *xsi_schemaLocation = NULL;
451   char szVersionBuf[OWS_VERSION_MAXLEN];
452 
453   xmlNsPtr     psNsXsi     = NULL;
454   xmlNodePtr   psRootNode  = NULL;
455   xmlNodePtr   psMainNode  = NULL;
456 
457   psRootNode = xmlNewNode(psNsOws, BAD_CAST "ExceptionReport");
458 
459   psNsXsi = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX);
460 
461   /* add attributes to root element */
462   xmlNewProp(psRootNode, BAD_CAST "version", BAD_CAST version);
463 
464   if (ows_version == OWS_1_0_0) {
465     xmlNewProp(psRootNode, BAD_CAST "language", BAD_CAST language);
466   }
467   if (ows_version == OWS_1_1_0) {
468     xmlNewProp(psRootNode, BAD_CAST "xml:lang", BAD_CAST language);
469   }
470 
471   xsi_schemaLocation = msStrdup((char *)psNsOws->href);
472   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, " ");
473   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, (char *)schemas_location);
474   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, "/ows/");
475   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, (char *)msOWSGetVersionString(ows_version, szVersionBuf));
476   xsi_schemaLocation = msStringConcatenate(xsi_schemaLocation, "/owsExceptionReport.xsd");
477 
478   /* add namespace'd attributes to root element */
479   xmlNewNsProp(psRootNode, psNsXsi, BAD_CAST "schemaLocation", BAD_CAST xsi_schemaLocation);
480 
481   /* add child element */
482   psMainNode = xmlNewChild(psRootNode, NULL, BAD_CAST "Exception", NULL);
483 
484   /* add attributes to child */
485   xmlNewProp(psMainNode, BAD_CAST "exceptionCode", BAD_CAST exceptionCode);
486 
487   if (locator != NULL) {
488     xmlNewProp(psMainNode, BAD_CAST "locator", BAD_CAST locator);
489   }
490 
491   if (ExceptionText != NULL) {
492     xmlNewTextChild(psMainNode, NULL, BAD_CAST "ExceptionText", BAD_CAST ExceptionText);
493   }
494 
495   free(xsi_schemaLocation);
496   return psRootNode;
497 }
498 
499 /**
500  * msOWSCommonBoundingBox()
501  *
502  * returns an object of BoundingBox as per subclause 10.2.1
503  *
504  * If necessary (ie. an EPSG URN GCS such as 4326) the tuple axes will be
505  * reoriented to match the EPSG coordinate system expectations.
506  *
507  * @param psNsOws OWS namespace object
508  * @param crs the CRS / EPSG code
509  * @param dimensions number of dimensions of the coordinates
510  * @param minx minx
511  * @param miny miny
512  * @param maxx maxx
513  * @param maxy maxy
514  *
515  * @return psRootNode xmlNodePtr pointer of XML construct
516  */
517 
msOWSCommonBoundingBox(xmlNsPtr psNsOws,const char * crs,int dimensions,double minx,double miny,double maxx,double maxy)518 xmlNodePtr msOWSCommonBoundingBox(xmlNsPtr psNsOws, const char *crs, int dimensions, double minx, double miny, double maxx, double maxy)
519 {
520   char LowerCorner[100];
521   char UpperCorner[100];
522   char dim_string[100];
523   xmlNodePtr psRootNode = NULL;
524 
525   /* Do we need to reorient tuple axes? */
526   if(crs && strstr(crs, "imageCRS") == NULL) {
527     projectionObj proj;
528 
529     msInitProjection( &proj );
530     if( msLoadProjectionString( &proj, (char *) crs ) == 0 ) {
531       msAxisNormalizePoints( &proj, 1, &minx, &miny );
532       msAxisNormalizePoints( &proj, 1, &maxx, &maxy );
533     }
534     msFreeProjection( &proj );
535   }
536 
537 
538   if (_validateNamespace(psNsOws) == MS_FAILURE)
539     psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
540 
541   /* create element name */
542   psRootNode = xmlNewNode(psNsOws, BAD_CAST "BoundingBox");
543 
544   /* add attributes to the root element */
545   xmlNewProp(psRootNode, BAD_CAST "crs", BAD_CAST crs);
546 
547   snprintf( dim_string, sizeof(dim_string), "%d", dimensions );
548   xmlNewProp(psRootNode, BAD_CAST "dimensions", BAD_CAST dim_string);
549 
550   snprintf(LowerCorner, sizeof(LowerCorner), "%.15g %.15g", minx, miny);
551   snprintf(UpperCorner, sizeof(UpperCorner), "%.15g %.15g", maxx, maxy);
552 
553   /* add child elements */
554   xmlNewChild(psRootNode, psNsOws,BAD_CAST "LowerCorner",BAD_CAST LowerCorner);
555   xmlNewChild(psRootNode, psNsOws,BAD_CAST "UpperCorner",BAD_CAST UpperCorner);
556 
557   return psRootNode;
558 }
559 
560 /**
561  * msOWSCommonWGS84BoundingBox()
562  *
563  * returns an object of WGS84BoundingBox as per subclause 10.2.2
564  *
565  * @param psNsOws OWS namespace object
566  * @param dimensions number of dimensions of the coordinates
567  * @param minx minx
568  * @param miny miny
569  * @param maxx maxx
570  * @param maxy maxy
571  *
572  * @return psRootNode xmlNodePtr pointer of XML construct
573  */
574 
msOWSCommonWGS84BoundingBox(xmlNsPtr psNsOws,int dimensions,double minx,double miny,double maxx,double maxy)575 xmlNodePtr msOWSCommonWGS84BoundingBox(xmlNsPtr psNsOws, int dimensions, double minx, double miny, double maxx, double maxy)
576 {
577   char LowerCorner[100];
578   char UpperCorner[100];
579   char dim_string[100];
580 
581   xmlNodePtr psRootNode = NULL;
582 
583   if (_validateNamespace(psNsOws) == MS_FAILURE)
584     psNsOws = xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
585 
586   /* create element name */
587   psRootNode = xmlNewNode(psNsOws, BAD_CAST "WGS84BoundingBox");
588 
589   snprintf( dim_string, sizeof(dim_string), "%d", dimensions );
590   xmlNewProp(psRootNode, BAD_CAST "dimensions", BAD_CAST dim_string);
591 
592   snprintf(LowerCorner, sizeof(LowerCorner), "%.15g %.15g", minx, miny);
593   snprintf(UpperCorner, sizeof(UpperCorner), "%.15g %.15g", maxx, maxy);
594 
595   /* add child elements */
596   xmlNewChild(psRootNode, psNsOws,BAD_CAST "LowerCorner",BAD_CAST LowerCorner);
597   xmlNewChild(psRootNode, psNsOws,BAD_CAST "UpperCorner",BAD_CAST UpperCorner);
598 
599   return psRootNode;
600 }
601 
602 /**
603  * _validateNamespace()
604  *
605  * validates the namespace passed to this module's functions
606  *
607  * @param psNsOws namespace object
608  *
609  * @return MS_SUCCESS or MS_FAILURE
610  *
611  */
612 
_validateNamespace(xmlNsPtr psNsOws)613 int _validateNamespace(xmlNsPtr psNsOws)
614 {
615   char namespace_prefix[10];
616   snprintf(namespace_prefix, sizeof(namespace_prefix), "%s", psNsOws->prefix);
617   if (strcmp(namespace_prefix, MS_OWSCOMMON_OWS_NAMESPACE_PREFIX) == 0)
618     return MS_SUCCESS;
619   else
620     return MS_FAILURE;
621 }
622 
623 /*
624  * Valid an xml string against an XML schema
625  * Inpired from: http://xml.developpez.com/sources/?page=validation#validate_XSD_CppCLI_2
626  * taken from tinyows.org
627  */
msOWSSchemaValidation(const char * xml_schema,const char * xml)628 int msOWSSchemaValidation(const char* xml_schema, const char* xml)
629 {
630   xmlSchemaPtr schema;
631   xmlSchemaParserCtxtPtr ctxt;
632   xmlSchemaValidCtxtPtr validctxt;
633   int ret;
634   xmlDocPtr doc;
635 
636   if (!xml_schema || !xml)
637     return MS_FAILURE;
638 
639   xmlInitParser();
640   schema = NULL;
641   ret = -1;
642 
643   /* To valide WFS 2.0 requests, we might need to explicitely import */
644   /* GML and FES 2.0 */
645   if( strlen(xml_schema) > strlen(MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION) &&
646       strcmp(xml_schema + strlen(xml_schema) -
647             strlen(MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION), MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION) == 0 )
648   {
649     char szInMemSchema[2048];
650     char szBaseLocation[256];
651 
652     strncpy(szBaseLocation, xml_schema, strlen(xml_schema) - strlen(MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION));
653     szBaseLocation[strlen(xml_schema) - strlen(MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION)] = '\0';
654 
655     strcpy(szInMemSchema, "<schema elementFormDefault=\"qualified\" version=\"1.0.0\" "
656                           "xmlns=\"http://www.w3.org/2001/XMLSchema\">\n");
657 
658     sprintf(szInMemSchema + strlen(szInMemSchema),
659             "<import namespace=\"%s\" schemaLocation=\"%s\" />\n",
660             MS_OWSCOMMON_WFS_20_NAMESPACE_URI, xml_schema);
661 
662     if( strstr(xml, MS_OWSCOMMON_FES_20_NAMESPACE_URI) != NULL )
663     {
664         sprintf(szInMemSchema + strlen(szInMemSchema),
665                 "<import namespace=\"%s\" schemaLocation=\"%s%s\" />\n",
666                 MS_OWSCOMMON_FES_20_NAMESPACE_URI, szBaseLocation, MS_OWSCOMMON_FES_20_SCHEMA_LOCATION);
667     }
668 
669     if( strstr(xml, MS_OWSCOMMON_GML_32_NAMESPACE_URI) != NULL )
670     {
671         sprintf(szInMemSchema + strlen(szInMemSchema),
672                 "<import namespace=\"%s\" schemaLocation=\"%s%s\" />\n",
673                 MS_OWSCOMMON_GML_32_NAMESPACE_URI, szBaseLocation, MS_OWSCOMMON_GML_321_SCHEMA_LOCATION);
674     }
675     else if( strstr(xml, MS_OWSCOMMON_GML_NAMESPACE_URI) != NULL )
676     {
677         if( strstr(xml, MS_OWSCOMMON_GML_212_SCHEMA_LOCATION) != NULL )
678         {
679             sprintf(szInMemSchema + strlen(szInMemSchema),
680                     "<import namespace=\"%s\" schemaLocation=\"%s%s\" />\n",
681                     MS_OWSCOMMON_GML_NAMESPACE_URI, szBaseLocation, MS_OWSCOMMON_GML_212_SCHEMA_LOCATION);
682         }
683         else if( strstr(xml, MS_OWSCOMMON_GML_311_SCHEMA_LOCATION) != NULL )
684         {
685             sprintf(szInMemSchema + strlen(szInMemSchema),
686                     "<import namespace=\"%s\" schemaLocation=\"%s%s\" />\n",
687                     MS_OWSCOMMON_GML_NAMESPACE_URI, szBaseLocation, MS_OWSCOMMON_GML_311_SCHEMA_LOCATION);
688         }
689     }
690 
691     strcat(szInMemSchema, "</schema>\n");
692     /*fprintf(stderr, "%s\n", szInMemSchema);*/
693 
694     ctxt = xmlSchemaNewMemParserCtxt(szInMemSchema, strlen(szInMemSchema));
695   }
696   else
697   {
698     /* Open XML Schema File */
699     ctxt = xmlSchemaNewParserCtxt(xml_schema);
700   }
701 
702   /*
703   xmlSchemaSetParserErrors(ctxt,
704                            (xmlSchemaValidityErrorFunc) libxml2_callback,
705                            (xmlSchemaValidityWarningFunc) libxml2_callback, stderr);
706   */
707 
708   schema = xmlSchemaParse(ctxt);
709   xmlSchemaFreeParserCtxt(ctxt);
710 
711   /* If XML Schema hasn't been rightly loaded */
712   if (schema == NULL) {
713     xmlSchemaCleanupTypes();
714     xmlMemoryDump();
715     xmlCleanupParser();
716     return ret;
717   }
718 
719   doc = xmlParseDoc((xmlChar *)xml);
720 
721   if (doc != NULL) {
722     /* Loading XML Schema content */
723     validctxt = xmlSchemaNewValidCtxt(schema);
724     /*
725     xmlSchemaSetValidErrors(validctxt,
726                             (xmlSchemaValidityErrorFunc) libxml2_callback,
727                             (xmlSchemaValidityWarningFunc) libxml2_callback, stderr);
728     */
729     /* validation */
730     ret = xmlSchemaValidateDoc(validctxt, doc);
731     xmlSchemaFreeValidCtxt(validctxt);
732   }
733 
734   xmlSchemaFree(schema);
735   xmlFreeDoc(doc);
736   xmlCleanupParser();
737 
738   return ret;
739 }
740 
741 #endif /* defined(USE_LIBXML2) */
742 
743 
744 /**
745  * msOWSCommonNegotiateVersion()
746  *
747  * returns a supported version as per subclause 7.3.2
748  *
749  * @param requested_version the version passed by the client
750  * @param supported_versions an array of supported versions
751  * @param num_supported_versions size of supported_versions
752  *
753  * @return supported version integer, or -1 on error
754  *
755  */
756 
msOWSCommonNegotiateVersion(int requested_version,const int supported_versions[],int num_supported_versions)757 int msOWSCommonNegotiateVersion(int requested_version, const int supported_versions[], int num_supported_versions)
758 {
759   int i;
760 
761   /* if version is not set return error */
762   if (! requested_version)
763     return -1;
764 
765   /* return the first entry that's equal to the requested version */
766   for (i = 0; i < num_supported_versions; i++) {
767     if (supported_versions[i] == requested_version)
768       return supported_versions[i];
769   }
770 
771   /* no match; calling code should throw an exception */
772   return -1;
773 }
774