/src/MapServer/src/mapows.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /******************************************************************************  | 
2  |  |  * $Id$  | 
3  |  |  *  | 
4  |  |  * Project:  MapServer  | 
5  |  |  * Purpose:  OGC Web Services (WMS, WFS) support functions  | 
6  |  |  * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)  | 
7  |  |  *  | 
8  |  |  ******************************************************************************  | 
9  |  |  * Copyright (c) 1996-2005 Regents of the University of Minnesota.  | 
10  |  |  *  | 
11  |  |  * Permission is hereby granted, free of charge, to any person obtaining a  | 
12  |  |  * copy of this software and associated documentation files (the "Software"),  | 
13  |  |  * to deal in the Software without restriction, including without limitation  | 
14  |  |  * the rights to use, copy, modify, merge, publish, distribute, sublicense,  | 
15  |  |  * and/or sell copies of the Software, and to permit persons to whom the  | 
16  |  |  * Software is furnished to do so, subject to the following conditions:  | 
17  |  |  *  | 
18  |  |  * The above copyright notice and this permission notice shall be included in  | 
19  |  |  * all copies of this Software or works derived from this Software.  | 
20  |  |  *  | 
21  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  | 
22  |  |  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  | 
23  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL  | 
24  |  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  | 
25  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  | 
26  |  |  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER  | 
27  |  |  * DEALINGS IN THE SOFTWARE.  | 
28  |  |  ****************************************************************************/  | 
29  |  |  | 
30  |  | #include "mapserver.h"  | 
31  |  | #include "maptime.h"  | 
32  |  | #include "maptemplate.h"  | 
33  |  | #include "mapows.h"  | 
34  |  |  | 
35  |  | #if defined(USE_LIBXML2)  | 
36  |  | #include "maplibxml2.h"  | 
37  |  | #else  | 
38  |  | #include "cpl_minixml.h"  | 
39  |  | #include "cpl_error.h"  | 
40  |  | #endif  | 
41  |  | #include "mapowscommon.h"  | 
42  |  |  | 
43  |  | #include <ctype.h> /* isalnum() */  | 
44  |  | #include <stdarg.h>  | 
45  |  | #include <assert.h>  | 
46  |  |  | 
47  |  | /*  | 
48  |  | ** msOWSInitRequestObj() initializes an owsRequestObj; i.e: sets  | 
49  |  | ** all internal pointers to NULL.  | 
50  |  | */  | 
51  | 0  | void msOWSInitRequestObj(owsRequestObj *ows_request) { | 
52  | 0  |   ows_request->numlayers = 0;  | 
53  | 0  |   ows_request->numwmslayerargs = 0;  | 
54  | 0  |   ows_request->enabled_layers = NULL;  | 
55  | 0  |   ows_request->layerwmsfilterindex = NULL;  | 
56  |  | 
  | 
57  | 0  |   ows_request->service = NULL;  | 
58  | 0  |   ows_request->version = NULL;  | 
59  | 0  |   ows_request->request = NULL;  | 
60  | 0  |   ows_request->document = NULL;  | 
61  | 0  | }  | 
62  |  |  | 
63  |  | /*  | 
64  |  | ** msOWSClearRequestObj() releases all resources associated with an  | 
65  |  | ** owsRequestObj.  | 
66  |  | */  | 
67  | 0  | void msOWSClearRequestObj(owsRequestObj *ows_request) { | 
68  | 0  |   msFree(ows_request->enabled_layers);  | 
69  | 0  |   msFree(ows_request->layerwmsfilterindex);  | 
70  | 0  |   msFree(ows_request->service);  | 
71  | 0  |   msFree(ows_request->version);  | 
72  | 0  |   msFree(ows_request->request);  | 
73  | 0  |   if (ows_request->document) { | 
74  | 0  | #if defined(USE_LIBXML2)  | 
75  | 0  |     xmlFreeDoc(ows_request->document);  | 
76  | 0  |     xmlCleanupParser();  | 
77  |  | #else  | 
78  |  |     CPLDestroyXMLNode(ows_request->document);  | 
79  |  | #endif  | 
80  | 0  |   }  | 
81  | 0  | }  | 
82  |  |  | 
83  |  | #if defined(USE_LIBXML2) && LIBXML_VERSION < 20900  | 
84  |  | static int bExternalEntityAsked = MS_FALSE;  | 
85  |  | static xmlParserInputPtr dummyEntityLoader(const char *URL, const char *ID,  | 
86  |  |                                            xmlParserCtxtPtr context) { | 
87  |  |   bExternalEntityAsked = MS_TRUE;  | 
88  |  |   return NULL;  | 
89  |  | }  | 
90  |  | #endif  | 
91  |  |  | 
92  |  | /*  | 
93  |  | ** msOWSPreParseRequest() parses a cgiRequestObj either with GET/KVP  | 
94  |  | ** or with POST/XML. Only SERVICE, VERSION (or WMTVER) and REQUEST are  | 
95  |  | ** being determined, all WxS (or SOS) specific parameters are parsed  | 
96  |  | ** within the according handler.  | 
97  |  | ** The results are saved within an owsRequestObj. If the request was  | 
98  |  | ** transmitted with POST/XML, either the document (if compiled with  | 
99  |  | ** libxml2) or the root CPLXMLNode is saved to the ows_request->document  | 
100  |  | ** field.  | 
101  |  | ** Returns MS_SUCCESS upon success, MS_FAILURE if severe errors occurred  | 
102  |  | ** or MS_DONE, if the service could not be determined.  | 
103  |  | */  | 
104  |  | static int msOWSPreParseRequest(cgiRequestObj *request,  | 
105  | 0  |                                 owsRequestObj *ows_request) { | 
106  |  |   /* decide if KVP or XML */  | 
107  | 0  |   if (request->type == MS_GET_REQUEST ||  | 
108  | 0  |       (request->type == MS_POST_REQUEST && request->contenttype &&  | 
109  | 0  |        strncmp(request->contenttype, "application/x-www-form-urlencoded",  | 
110  | 0  |                strlen("application/x-www-form-urlencoded")) == 0)) { | 
111  | 0  |     int i;  | 
112  |  |     /* parse KVP parameters service, version and request */  | 
113  | 0  |     for (i = 0; i < request->NumParams; ++i) { | 
114  | 0  |       if (ows_request->service == NULL &&  | 
115  | 0  |           EQUAL(request->ParamNames[i], "SERVICE")) { | 
116  | 0  |         ows_request->service = msStrdup(request->ParamValues[i]);  | 
117  | 0  |       } else if (ows_request->version == NULL &&  | 
118  | 0  |                  (EQUAL(request->ParamNames[i], "VERSION") ||  | 
119  | 0  |                   EQUAL(request->ParamNames[i], "WMTVER"))) { /* for WMS */ | 
120  | 0  |         ows_request->version = msStrdup(request->ParamValues[i]);  | 
121  | 0  |       } else if (ows_request->request == NULL &&  | 
122  | 0  |                  EQUAL(request->ParamNames[i], "REQUEST")) { | 
123  | 0  |         ows_request->request = msStrdup(request->ParamValues[i]);  | 
124  | 0  |       }  | 
125  |  |  | 
126  |  |       /* stop if we have all necessary parameters */  | 
127  | 0  |       if (ows_request->service && ows_request->version &&  | 
128  | 0  |           ows_request->request) { | 
129  | 0  |         break;  | 
130  | 0  |       }  | 
131  | 0  |     }  | 
132  | 0  |   } else if (request->type == MS_POST_REQUEST) { | 
133  | 0  | #if defined(USE_LIBXML2)  | 
134  | 0  |     xmlNodePtr root = NULL;  | 
135  |  | #if LIBXML_VERSION < 20900  | 
136  |  |     xmlExternalEntityLoader oldExternalEntityLoader;  | 
137  |  | #endif  | 
138  |  | #else  | 
139  |  |     CPLXMLNode *temp;  | 
140  |  | #endif  | 
141  | 0  |     if (!request->postrequest || !strlen(request->postrequest)) { | 
142  | 0  |       msSetError(MS_OWSERR, "POST request is empty.", "msOWSPreParseRequest()");  | 
143  | 0  |       return MS_FAILURE;  | 
144  | 0  |     }  | 
145  | 0  | #if defined(USE_LIBXML2)  | 
146  |  | #if LIBXML_VERSION < 20900  | 
147  |  |     oldExternalEntityLoader = xmlGetExternalEntityLoader();  | 
148  |  |     /* to avoid  XML External Entity vulnerability with libxml2 < 2.9 */  | 
149  |  |     xmlSetExternalEntityLoader(dummyEntityLoader);  | 
150  |  |     bExternalEntityAsked = MS_FALSE;  | 
151  |  | #endif  | 
152  |  |     /* parse to DOM-Structure with libxml2 and get the root element */  | 
153  | 0  |     ows_request->document =  | 
154  | 0  |         xmlParseMemory(request->postrequest, strlen(request->postrequest));  | 
155  |  | #if LIBXML_VERSION < 20900  | 
156  |  |     xmlSetExternalEntityLoader(oldExternalEntityLoader);  | 
157  |  |     if (bExternalEntityAsked) { | 
158  |  |       msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",  | 
159  |  |                  "External entity fetch");  | 
160  |  |       return MS_FAILURE;  | 
161  |  |     }  | 
162  |  | #endif  | 
163  | 0  |     if (ows_request->document == NULL ||  | 
164  | 0  |         (root = xmlDocGetRootElement(ows_request->document)) == NULL) { | 
165  | 0  |       const xmlError *error = xmlGetLastError();  | 
166  | 0  |       msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",  | 
167  | 0  |                  error->message);  | 
168  | 0  |       return MS_FAILURE;  | 
169  | 0  |     }  | 
170  |  |  | 
171  |  |     /* Get service, version and request from root */  | 
172  | 0  |     xmlChar *serviceTmp = xmlGetProp(root, BAD_CAST "service");  | 
173  | 0  |     if (serviceTmp != NULL) { | 
174  | 0  |       ows_request->service = msStrdup((char *)serviceTmp);  | 
175  | 0  |       xmlFree(serviceTmp);  | 
176  | 0  |     }  | 
177  |  | 
  | 
178  | 0  |     xmlChar *versionTmp = xmlGetProp(root, BAD_CAST "version");  | 
179  | 0  |     if (versionTmp != NULL) { | 
180  | 0  |       ows_request->version = msStrdup((char *)versionTmp);  | 
181  | 0  |       xmlFree(versionTmp);  | 
182  | 0  |     }  | 
183  |  | 
  | 
184  | 0  |     ows_request->request = msStrdup((char *)root->name);  | 
185  |  | 
  | 
186  |  | #else  | 
187  |  |     /* parse with CPLXML */  | 
188  |  |     ows_request->document = CPLParseXMLString(request->postrequest);  | 
189  |  |     if (ows_request->document == NULL) { | 
190  |  |       msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",  | 
191  |  |                  CPLGetLastErrorMsg());  | 
192  |  |       return MS_FAILURE;  | 
193  |  |     }  | 
194  |  |  | 
195  |  |     /* remove all namespaces */  | 
196  |  |     CPLStripXMLNamespace(ows_request->document, NULL, 1);  | 
197  |  |     for (temp = ows_request->document; temp != NULL; temp = temp->psNext) { | 
198  |  |  | 
199  |  |       if (temp->eType == CXT_Element) { | 
200  |  |         const char *service, *version;  | 
201  |  |         ows_request->request = msStrdup(temp->pszValue);  | 
202  |  |  | 
203  |  |         if ((service = CPLGetXMLValue(temp, "service", NULL)) != NULL) { | 
204  |  |           ows_request->service = msStrdup(service);  | 
205  |  |         }  | 
206  |  |         if ((version = CPLGetXMLValue(temp, "version", NULL)) != NULL) { | 
207  |  |           ows_request->version = msStrdup(version);  | 
208  |  |         }  | 
209  |  |         continue;  | 
210  |  |       }  | 
211  |  |     }  | 
212  |  | #endif /* defined(USE_LIBXML2) */  | 
213  | 0  |   } else { | 
214  | 0  |     msSetError(MS_OWSERR, "Unknown request method. Use either GET or POST.",  | 
215  | 0  |                "msOWSPreParseRequest()");  | 
216  | 0  |     return MS_FAILURE;  | 
217  | 0  |   }  | 
218  |  |  | 
219  |  |   /* certain WMS requests do not require the service parameter */  | 
220  |  |   /* see: http://trac.osgeo.org/mapserver/ticket/2531          */  | 
221  | 0  |   if (ows_request->service == NULL && ows_request->request != NULL) { | 
222  | 0  |     if (EQUAL(ows_request->request, "GetMap") ||  | 
223  | 0  |         EQUAL(ows_request->request, "GetFeatureInfo")) { | 
224  | 0  |       ows_request->service = msStrdup("WMS"); | 
225  | 0  |     } else { /* service could not be determined */ | 
226  | 0  |       return MS_DONE;  | 
227  | 0  |     }  | 
228  | 0  |   }  | 
229  |  |  | 
230  | 0  |   return MS_SUCCESS;  | 
231  | 0  | }  | 
232  |  |  | 
233  |  | /*  | 
234  |  | ** msOWSDispatch() is the entry point for any OWS request (WMS, WFS, ...)  | 
235  |  | ** - If this is a valid request then it is processed and MS_SUCCESS is returned  | 
236  |  | **   on success, or MS_FAILURE on failure.  | 
237  |  | ** - If force_ows_mode is true then an exception will be produced if the  | 
238  |  | **   request is not recognized as a valid request.  | 
239  |  | ** - If force_ows_mode is false and this does not appear to be a valid OWS  | 
240  |  | **   request then MS_DONE is returned and MapServer is expected to process  | 
241  |  | **   this as a regular MapServer (traditional CGI) request.  | 
242  |  | */  | 
243  | 0  | int msOWSDispatch(mapObj *map, cgiRequestObj *request, int ows_mode) { | 
244  | 0  |   int status = MS_DONE, force_ows_mode = 0;  | 
245  | 0  |   owsRequestObj ows_request;  | 
246  |  | 
  | 
247  | 0  |   if (!request) { | 
248  | 0  |     return status;  | 
249  | 0  |   }  | 
250  |  |  | 
251  | 0  |   force_ows_mode = (ows_mode == OWS || ows_mode == WFS);  | 
252  |  | 
  | 
253  | 0  |   msOWSInitRequestObj(&ows_request);  | 
254  | 0  |   switch (msOWSPreParseRequest(request, &ows_request)) { | 
255  | 0  |   case MS_FAILURE: /* a severe error occurred */  | 
256  | 0  |     return MS_FAILURE;  | 
257  | 0  |   case MS_DONE:  | 
258  |  |     /* OWS Service could not be determined              */  | 
259  |  |     /* continue for now                                 */  | 
260  | 0  |     status = MS_DONE;  | 
261  | 0  |   }  | 
262  |  |  | 
263  | 0  |   if (ows_request.service == NULL) { | 
264  | 0  | #ifdef USE_LIBXML2  | 
265  | 0  |     if (ows_request.request && EQUAL(ows_request.request, "GetMetadata")) { | 
266  | 0  |       status = msMetadataDispatch(map, request);  | 
267  | 0  |       msOWSClearRequestObj(&ows_request);  | 
268  | 0  |       return status;  | 
269  | 0  |     }  | 
270  | 0  | #endif  | 
271  | 0  | #ifdef USE_WFS_SVR  | 
272  | 0  |     if (msOWSLookupMetadata(&(map->web.metadata), "FO", "cite_wfs2") != NULL) { | 
273  | 0  |       status = msWFSException(map, "service",  | 
274  | 0  |                               MS_OWS_ERROR_MISSING_PARAMETER_VALUE, NULL);  | 
275  | 0  |     } else  | 
276  | 0  | #endif  | 
277  |  |  | 
278  |  |       /* exit if service is not set */  | 
279  | 0  |       if (force_ows_mode) { | 
280  | 0  |         msSetError(MS_MISCERR,  | 
281  | 0  |                    "OWS Common exception: exceptionCode=MissingParameterValue, "  | 
282  | 0  |                    "locator=SERVICE, ExceptionText=SERVICE parameter missing.",  | 
283  | 0  |                    "msOWSDispatch()");  | 
284  | 0  |         status = MS_FAILURE;  | 
285  | 0  |       } else { | 
286  | 0  |         status = MS_DONE;  | 
287  | 0  |       }  | 
288  | 0  |   } else if (EQUAL(ows_request.service, "WMS")) { | 
289  | 0  | #ifdef USE_WMS_SVR  | 
290  | 0  |     status = msWMSDispatch(map, request, &ows_request, MS_FALSE);  | 
291  |  | #else  | 
292  |  |     msSetError(  | 
293  |  |         MS_WMSERR,  | 
294  |  |         "SERVICE=WMS requested, but WMS support not configured in MapServer.",  | 
295  |  |         "msOWSDispatch()");  | 
296  |  | #endif  | 
297  | 0  |   } else if (EQUAL(ows_request.service, "WFS")) { | 
298  | 0  | #ifdef USE_WFS_SVR  | 
299  | 0  |     status = msWFSDispatch(map, request, &ows_request, (ows_mode == WFS));  | 
300  |  | #else  | 
301  |  |     msSetError(  | 
302  |  |         MS_WFSERR,  | 
303  |  |         "SERVICE=WFS requested, but WFS support not configured in MapServer.",  | 
304  |  |         "msOWSDispatch()");  | 
305  |  | #endif  | 
306  | 0  |   } else if (EQUAL(ows_request.service, "WCS")) { | 
307  | 0  | #ifdef USE_WCS_SVR  | 
308  | 0  |     status = msWCSDispatch(map, request, &ows_request);  | 
309  |  | #else  | 
310  |  |     msSetError(  | 
311  |  |         MS_WCSERR,  | 
312  |  |         "SERVICE=WCS requested, but WCS support not configured in MapServer.",  | 
313  |  |         "msOWSDispatch()");  | 
314  |  | #endif  | 
315  | 0  |   } else if (EQUAL(ows_request.service, "SOS")) { | 
316  |  | #ifdef USE_SOS_SVR  | 
317  |  |     status = msSOSDispatch(map, request, &ows_request);  | 
318  |  | #else  | 
319  | 0  |     msSetError(  | 
320  | 0  |         MS_SOSERR,  | 
321  | 0  |         "SERVICE=SOS requested, but SOS support not configured in MapServer.",  | 
322  | 0  |         "msOWSDispatch()");  | 
323  | 0  | #endif  | 
324  | 0  |   } else if (force_ows_mode) { | 
325  | 0  |     msSetError(  | 
326  | 0  |         MS_MISCERR,  | 
327  | 0  |         "OWS Common exception: exceptionCode=InvalidParameterValue, "  | 
328  | 0  |         "locator=SERVICE, ExceptionText=SERVICE parameter value invalid.",  | 
329  | 0  |         "msOWSDispatch()");  | 
330  | 0  |     status = MS_FAILURE;  | 
331  | 0  |   }  | 
332  |  |  | 
333  | 0  |   msOWSClearRequestObj(&ows_request);  | 
334  | 0  |   return status;  | 
335  | 0  | }  | 
336  |  |  | 
337  |  | /*  | 
338  |  | ** msOWSIpParse()  | 
339  |  | **  | 
340  |  | ** Parse the IP address or range into a binary array.  | 
341  |  | ** Supports ipv4 and ipv6 addresses  | 
342  |  | ** Ranges can be specified using the CIDR notation (ie: 192.100.100.0/24)  | 
343  |  | **  | 
344  |  | ** Returns the parsed of the IP (4 or 16).  | 
345  |  | */  | 
346  | 0  | int msOWSIpParse(const char *ip, unsigned char *ip1, unsigned char *mask) { | 
347  | 0  |   int len = 0, masklen, seps;  | 
348  |  | 
  | 
349  | 0  |   if (msCountChars((char *)ip, '.') == 3) { | 
350  |  |     /* ipv4 */  | 
351  | 0  |     unsigned char *val = ip1;  | 
352  | 0  |     len = 1;  | 
353  | 0  |     masklen = 32;  | 
354  | 0  |     *val = 0;  | 
355  | 0  |     while (*ip) { | 
356  | 0  |       if (*ip >= '0' && *ip <= '9')  | 
357  | 0  |         (*val) = 10 * (*val) + (*ip - '0');  | 
358  | 0  |       else if (*ip == '.') { | 
359  | 0  |         ++val;  | 
360  | 0  |         *val = 0;  | 
361  | 0  |         ++len;  | 
362  | 0  |       } else if (*ip == '/') { | 
363  | 0  |         masklen = atoi(ip + 1);  | 
364  | 0  |         if (masklen > 32)  | 
365  | 0  |           masklen = 32;  | 
366  | 0  |         break;  | 
367  | 0  |       } else  | 
368  | 0  |         break;  | 
369  | 0  |       ++ip;  | 
370  | 0  |     }  | 
371  | 0  |     if (len != 4)  | 
372  | 0  |       return 0;  | 
373  |  |     /* write mask */  | 
374  | 0  |     if (mask) { | 
375  | 0  |       memset(mask, 0, len);  | 
376  | 0  |       val = mask;  | 
377  | 0  |       while (masklen) { | 
378  | 0  |         if (masklen >= 8) { | 
379  | 0  |           *val = 0xff;  | 
380  | 0  |           masklen -= 8;  | 
381  | 0  |         } else { | 
382  | 0  |           *val = -((unsigned char)pow(2, 8 - masklen));  | 
383  | 0  |           break;  | 
384  | 0  |         }  | 
385  | 0  |         ++val;  | 
386  | 0  |       }  | 
387  | 0  |     }  | 
388  | 0  |   } else if ((seps = msCountChars((char *)ip, ':')) > 1 && seps < 8) { | 
389  |  |     /* ipv6 */  | 
390  | 0  |     unsigned short *val = (unsigned short *)ip1;  | 
391  | 0  |     len = 2;  | 
392  | 0  |     masklen = 128;  | 
393  | 0  |     *val = 0;  | 
394  | 0  |     while (*ip) { | 
395  | 0  |       if (*ip >= '0' && *ip <= '9')  | 
396  | 0  |         (*val) = 16 * (*val) + (*ip - '0');  | 
397  | 0  |       else if (*ip >= 'a' && *ip <= 'f')  | 
398  | 0  |         (*val) = 16 * (*val) + (*ip - 'a' + 10);  | 
399  | 0  |       else if (*ip >= 'A' && *ip <= 'F')  | 
400  | 0  |         (*val) = 16 * (*val) + (*ip - 'A' + 10);  | 
401  | 0  |       else if (*ip == ':') { | 
402  | 0  |         ++ip;  | 
403  | 0  |         ++val;  | 
404  | 0  |         len += 2;  | 
405  | 0  |         *val = 0;  | 
406  | 0  |         if (*ip == ':') { | 
407  |  |           /* insert 0 values */  | 
408  | 0  |           while (seps <= 7) { | 
409  | 0  |             ++val;  | 
410  | 0  |             len += 2;  | 
411  | 0  |             *val = 0;  | 
412  | 0  |             ++seps;  | 
413  | 0  |           }  | 
414  | 0  |         } else  | 
415  | 0  |           continue;  | 
416  | 0  |       } else if (*ip == '/') { | 
417  | 0  |         masklen = atoi(ip + 1);  | 
418  | 0  |         if (masklen > 128)  | 
419  | 0  |           masklen = 128;  | 
420  | 0  |         break;  | 
421  | 0  |       } else  | 
422  | 0  |         break;  | 
423  | 0  |       ++ip;  | 
424  | 0  |     }  | 
425  | 0  |     if (len != 16)  | 
426  | 0  |       return 0;  | 
427  |  |     /* write mask */  | 
428  | 0  |     if (mask) { | 
429  | 0  |       memset(mask, 0, len);  | 
430  | 0  |       val = (unsigned short *)mask;  | 
431  | 0  |       while (masklen) { | 
432  | 0  |         if (masklen >= 16) { | 
433  | 0  |           *val = 0xffff;  | 
434  | 0  |           masklen -= 16;  | 
435  | 0  |         } else { | 
436  | 0  |           *val = -((unsigned short)pow(2, 16 - masklen));  | 
437  | 0  |           break;  | 
438  | 0  |         }  | 
439  | 0  |         ++val;  | 
440  | 0  |       }  | 
441  | 0  |     }  | 
442  | 0  |   }  | 
443  |  |  | 
444  | 0  |   return len;  | 
445  | 0  | }  | 
446  |  |  | 
447  |  | /*  | 
448  |  | ** msOWSIpInList()  | 
449  |  | **  | 
450  |  | ** Check if an ip is in a space separated list of IP addresses/ranges.  | 
451  |  | ** Supports ipv4 and ipv6 addresses  | 
452  |  | ** Ranges can be specified using the CIDR notation (ie: 192.100.100.0/24)  | 
453  |  | **  | 
454  |  | ** Returns MS_TRUE if the IP is found.  | 
455  |  | */  | 
456  | 0  | int msOWSIpInList(const char *ip_list, const char *ip) { | 
457  | 0  |   int i, j, numips, iplen;  | 
458  | 0  |   unsigned char ip1[16];  | 
459  | 0  |   unsigned char ip2[16];  | 
460  | 0  |   unsigned char mask[16];  | 
461  | 0  |   char **ips;  | 
462  |  |  | 
463  |  |   /* Parse input IP */  | 
464  | 0  |   iplen = msOWSIpParse(ip, (unsigned char *)&ip1, NULL);  | 
465  | 0  |   if (iplen != 4 && iplen != 16) /* ipv4 or ipv6 */  | 
466  | 0  |     return MS_FALSE;  | 
467  |  |  | 
468  | 0  |   ips = msStringSplit(ip_list, ' ', &numips);  | 
469  | 0  |   if (ips) { | 
470  | 0  |     for (i = 0; i < numips; i++) { | 
471  | 0  |       if (msOWSIpParse(ips[i], (unsigned char *)&ip2, (unsigned char *)&mask) ==  | 
472  | 0  |           iplen) { | 
473  | 0  |         for (j = 0; j < iplen; j++) { | 
474  | 0  |           if ((ip1[j] & mask[j]) != (ip2[j] & mask[j]))  | 
475  | 0  |             break;  | 
476  | 0  |         }  | 
477  | 0  |         if (j == iplen) { | 
478  | 0  |           msFreeCharArray(ips, numips);  | 
479  | 0  |           return MS_TRUE; /* match found */  | 
480  | 0  |         }  | 
481  | 0  |       }  | 
482  | 0  |     }  | 
483  | 0  |     msFreeCharArray(ips, numips);  | 
484  | 0  |   }  | 
485  |  |  | 
486  | 0  |   return MS_FALSE;  | 
487  | 0  | }  | 
488  |  |  | 
489  |  | /*  | 
490  |  | ** msOWSIpDisabled()  | 
491  |  | **  | 
492  |  | ** Check if an ip is in a list specified in the metadata section.  | 
493  |  | **  | 
494  |  | ** Returns MS_TRUE if the IP is found.  | 
495  |  | */  | 
496  | 0  | int msOWSIpInMetadata(const char *ip_list, const char *ip) { | 
497  | 0  |   FILE *stream;  | 
498  | 0  |   char buffer[MS_BUFFER_LENGTH];  | 
499  | 0  |   int found = MS_FALSE;  | 
500  |  | 
  | 
501  | 0  |   if (strncasecmp(ip_list, "file:", 5) == 0) { | 
502  | 0  |     stream = fopen(ip_list + 5, "r");  | 
503  | 0  |     if (stream) { | 
504  | 0  |       found = MS_FALSE;  | 
505  | 0  |       while (fgets(buffer, MS_BUFFER_LENGTH, stream)) { | 
506  | 0  |         if (msOWSIpInList(buffer, ip)) { | 
507  | 0  |           found = MS_TRUE;  | 
508  | 0  |           break;  | 
509  | 0  |         }  | 
510  | 0  |       }  | 
511  | 0  |       fclose(stream);  | 
512  | 0  |     }  | 
513  | 0  |   } else { | 
514  | 0  |     if (msOWSIpInList(ip_list, ip))  | 
515  | 0  |       found = MS_TRUE;  | 
516  | 0  |   }  | 
517  | 0  |   return found;  | 
518  | 0  | }  | 
519  |  |  | 
520  |  | /*  | 
521  |  | ** msOWSIpDisabled()  | 
522  |  | **  | 
523  |  | ** Check if the layers are enabled or disabled by IP list.  | 
524  |  | **  | 
525  |  | ** 'namespaces' is a string with a letter for each namespace to lookup  | 
526  |  | ** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_  | 
527  |  | ** If namespaces is NULL then this function just does a regular metadata  | 
528  |  | ** lookup.  | 
529  |  | **  | 
530  |  | ** Returns the disabled flag.  | 
531  |  | */  | 
532  |  | int msOWSIpDisabled(hashTableObj *metadata, const char *namespaces,  | 
533  | 0  |                     const char *ip) { | 
534  | 0  |   const char *ip_list;  | 
535  | 0  |   int disabled = MS_FALSE;  | 
536  |  | 
  | 
537  | 0  |   if (!ip)  | 
538  | 0  |     return MS_FALSE; /* no endpoint ip */  | 
539  |  |  | 
540  | 0  |   ip_list = msOWSLookupMetadata(metadata, namespaces, "allowed_ip_list");  | 
541  | 0  |   if (!ip_list)  | 
542  | 0  |     ip_list = msOWSLookupMetadata(metadata, "O", "allowed_ip_list");  | 
543  |  | 
  | 
544  | 0  |   if (ip_list) { | 
545  | 0  |     disabled = MS_TRUE;  | 
546  | 0  |     if (msOWSIpInMetadata(ip_list, ip))  | 
547  | 0  |       disabled = MS_FALSE;  | 
548  | 0  |   }  | 
549  |  | 
  | 
550  | 0  |   ip_list = msOWSLookupMetadata(metadata, namespaces, "denied_ip_list");  | 
551  | 0  |   if (!ip_list)  | 
552  | 0  |     ip_list = msOWSLookupMetadata(metadata, "O", "denied_ip_list");  | 
553  |  | 
  | 
554  | 0  |   if (ip_list && msOWSIpInMetadata(ip_list, ip))  | 
555  | 0  |     disabled = MS_TRUE;  | 
556  |  | 
  | 
557  | 0  |   return disabled;  | 
558  | 0  | }  | 
559  |  |  | 
560  |  | /*  | 
561  |  | ** msOWSRequestIsEnabled()  | 
562  |  | **  | 
563  |  | ** Check if a layer is visible for a specific OWS request.  | 
564  |  | **  | 
565  |  | ** 'namespaces' is a string with a letter for each namespace to lookup in  | 
566  |  | ** the order they should be looked up. e.g. "MO" to lookup wms_ and ows_ If  | 
567  |  | ** namespaces is NULL then this function just does a regular metadata  | 
568  |  | ** lookup. If check_all_layers is set to MS_TRUE, the function will check  | 
569  |  | ** all layers to see if the request is enable. (returns as soon as one is found)  | 
570  |  | */  | 
571  |  | int msOWSRequestIsEnabled(mapObj *map, layerObj *layer, const char *namespaces,  | 
572  | 0  |                           const char *request, int check_all_layers) { | 
573  | 0  |   int disabled = MS_FALSE; /* explicitly disabled flag */  | 
574  | 0  |   const char *enable_request;  | 
575  | 0  |   const char *remote_ip;  | 
576  |  | 
  | 
577  | 0  |   if (request == NULL)  | 
578  | 0  |     return MS_FALSE;  | 
579  |  |  | 
580  | 0  |   remote_ip = getenv("REMOTE_ADDR"); | 
581  |  |  | 
582  |  |   /* First, we check in the layer metadata */  | 
583  | 0  |   if (layer && check_all_layers == MS_FALSE) { | 
584  | 0  |     enable_request =  | 
585  | 0  |         msOWSLookupMetadata(&layer->metadata, namespaces, "enable_request");  | 
586  | 0  |     if (msOWSParseRequestMetadata(enable_request, request, &disabled))  | 
587  | 0  |       return MS_TRUE;  | 
588  | 0  |     if (disabled)  | 
589  | 0  |       return MS_FALSE;  | 
590  |  |  | 
591  | 0  |     enable_request =  | 
592  | 0  |         msOWSLookupMetadata(&layer->metadata, "O", "enable_request");  | 
593  | 0  |     if (msOWSParseRequestMetadata(enable_request, request, &disabled))  | 
594  | 0  |       return MS_TRUE;  | 
595  | 0  |     if (disabled)  | 
596  | 0  |       return MS_FALSE;  | 
597  |  |  | 
598  | 0  |     if (msOWSIpDisabled(&layer->metadata, namespaces, remote_ip))  | 
599  | 0  |       return MS_FALSE;  | 
600  | 0  |   }  | 
601  |  |  | 
602  | 0  |   if (map && (check_all_layers == MS_FALSE || map->numlayers == 0)) { | 
603  |  |     /* then we check in the map metadata */  | 
604  | 0  |     enable_request =  | 
605  | 0  |         msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");  | 
606  | 0  |     if (msOWSParseRequestMetadata(enable_request, request, &disabled))  | 
607  | 0  |       return MS_TRUE;  | 
608  | 0  |     if (disabled)  | 
609  | 0  |       return MS_FALSE;  | 
610  |  |  | 
611  | 0  |     enable_request =  | 
612  | 0  |         msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");  | 
613  | 0  |     if (msOWSParseRequestMetadata(enable_request, request, &disabled))  | 
614  | 0  |       return MS_TRUE;  | 
615  | 0  |     if (disabled)  | 
616  | 0  |       return MS_FALSE;  | 
617  |  |  | 
618  | 0  |     if (msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))  | 
619  | 0  |       return MS_FALSE;  | 
620  | 0  |   }  | 
621  |  |  | 
622  | 0  |   if (map && check_all_layers == MS_TRUE) { | 
623  | 0  |     int i, globally_enabled = MS_FALSE;  | 
624  | 0  |     enable_request =  | 
625  | 0  |         msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");  | 
626  | 0  |     globally_enabled =  | 
627  | 0  |         msOWSParseRequestMetadata(enable_request, request, &disabled);  | 
628  |  | 
  | 
629  | 0  |     if (!globally_enabled && !disabled) { | 
630  | 0  |       enable_request =  | 
631  | 0  |           msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");  | 
632  | 0  |       globally_enabled =  | 
633  | 0  |           msOWSParseRequestMetadata(enable_request, request, &disabled);  | 
634  | 0  |     }  | 
635  |  | 
  | 
636  | 0  |     if (globally_enabled &&  | 
637  | 0  |         msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))  | 
638  | 0  |       globally_enabled = MS_FALSE;  | 
639  |  |  | 
640  |  |     /* Check all layers */  | 
641  | 0  |     for (i = 0; i < map->numlayers; i++) { | 
642  | 0  |       int result = MS_FALSE;  | 
643  | 0  |       layerObj *lp;  | 
644  | 0  |       lp = (GET_LAYER(map, i));  | 
645  |  | 
  | 
646  | 0  |       enable_request =  | 
647  | 0  |           msOWSLookupMetadata(&lp->metadata, namespaces, "enable_request");  | 
648  | 0  |       result = msOWSParseRequestMetadata(enable_request, request, &disabled);  | 
649  | 0  |       if (!result && disabled)  | 
650  | 0  |         continue; /* if the request has been explicitly set to disabled,  | 
651  |  |                      continue */  | 
652  |  |  | 
653  | 0  |       if (!result && !disabled) { /* if the request has not been found in the | 
654  |  |                                      wms metadata, */  | 
655  |  |         /* check the ows namespace  */  | 
656  |  | 
  | 
657  | 0  |         enable_request =  | 
658  | 0  |             msOWSLookupMetadata(&lp->metadata, "O", "enable_request");  | 
659  | 0  |         result = msOWSParseRequestMetadata(enable_request, request, &disabled);  | 
660  | 0  |         if (!result && disabled)  | 
661  | 0  |           continue;  | 
662  | 0  |       }  | 
663  |  |  | 
664  | 0  |       if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))  | 
665  | 0  |         continue;  | 
666  |  |  | 
667  | 0  |       if (result || (!disabled && globally_enabled))  | 
668  | 0  |         return MS_TRUE;  | 
669  | 0  |     }  | 
670  |  |  | 
671  | 0  |     if (!disabled && globally_enabled)  | 
672  | 0  |       return MS_TRUE;  | 
673  | 0  |   }  | 
674  |  |  | 
675  | 0  |   return MS_FALSE;  | 
676  | 0  | }  | 
677  |  |  | 
678  |  | /*  | 
679  |  | ** msOWSRequestLayersEnabled()  | 
680  |  | **  | 
681  |  | ** Check if the layers are visible for a specific OWS request.  | 
682  |  | **  | 
683  |  | ** 'namespaces' is a string with a letter for each namespace to lookup  | 
684  |  | ** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_  | 
685  |  | ** If namespaces is NULL then this function just does a regular metadata  | 
686  |  | ** lookup.  | 
687  |  | **  | 
688  |  | ** Generates an array of the layer ids enabled.  | 
689  |  | */  | 
690  |  | void msOWSRequestLayersEnabled(mapObj *map, const char *namespaces,  | 
691  |  |                                const char *request,  | 
692  | 0  |                                owsRequestObj *ows_request) { | 
693  | 0  |   int disabled = MS_FALSE; /* explicitly disabled flag */  | 
694  | 0  |   int globally_enabled = MS_FALSE;  | 
695  | 0  |   const char *enable_request;  | 
696  | 0  |   const char *remote_ip;  | 
697  |  | 
  | 
698  | 0  |   if (ows_request->numlayers > 0)  | 
699  | 0  |     msFree(ows_request->enabled_layers);  | 
700  |  | 
  | 
701  | 0  |   ows_request->numlayers = 0;  | 
702  | 0  |   ows_request->enabled_layers = NULL;  | 
703  |  | 
  | 
704  | 0  |   if (request == NULL || (map == NULL) || (map->numlayers <= 0))  | 
705  | 0  |     return;  | 
706  |  |  | 
707  | 0  |   remote_ip = getenv("REMOTE_ADDR"); | 
708  |  | 
  | 
709  | 0  |   enable_request =  | 
710  | 0  |       msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");  | 
711  | 0  |   globally_enabled =  | 
712  | 0  |       msOWSParseRequestMetadata(enable_request, request, &disabled);  | 
713  |  | 
  | 
714  | 0  |   if (!globally_enabled && !disabled) { | 
715  | 0  |     enable_request =  | 
716  | 0  |         msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");  | 
717  | 0  |     globally_enabled =  | 
718  | 0  |         msOWSParseRequestMetadata(enable_request, request, &disabled);  | 
719  | 0  |   }  | 
720  |  | 
  | 
721  | 0  |   if (globally_enabled &&  | 
722  | 0  |       msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))  | 
723  | 0  |     globally_enabled = MS_FALSE;  | 
724  |  | 
  | 
725  | 0  |   if (map->numlayers) { | 
726  | 0  |     int i, layers_size = map->numlayers; /* for most of cases, this will be  | 
727  |  |                                             relatively small */  | 
728  |  | 
  | 
729  | 0  |     ows_request->enabled_layers =  | 
730  | 0  |         (int *)msSmallMalloc(sizeof(int) * layers_size);  | 
731  |  | 
  | 
732  | 0  |     for (i = 0; i < map->numlayers; i++) { | 
733  | 0  |       int result = MS_FALSE;  | 
734  | 0  |       layerObj *lp;  | 
735  | 0  |       lp = (GET_LAYER(map, i));  | 
736  |  | 
  | 
737  | 0  |       enable_request =  | 
738  | 0  |           msOWSLookupMetadata(&lp->metadata, namespaces, "enable_request");  | 
739  | 0  |       result = msOWSParseRequestMetadata(enable_request, request, &disabled);  | 
740  | 0  |       if (!result && disabled)  | 
741  | 0  |         continue; /* if the request has been explicitly set to disabled,  | 
742  |  |                      continue */  | 
743  |  |  | 
744  | 0  |       if (!result && !disabled) { /* if the request has not been found in the | 
745  |  |                                      wms metadata, */  | 
746  |  |         /* check the ows namespace  */  | 
747  |  | 
  | 
748  | 0  |         enable_request =  | 
749  | 0  |             msOWSLookupMetadata(&lp->metadata, "O", "enable_request");  | 
750  | 0  |         result = msOWSParseRequestMetadata(enable_request, request, &disabled);  | 
751  | 0  |         if (!result && disabled)  | 
752  | 0  |           continue;  | 
753  | 0  |       }  | 
754  |  |  | 
755  | 0  |       if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))  | 
756  | 0  |         continue;  | 
757  |  |  | 
758  | 0  |       if (result || (!disabled && globally_enabled)) { | 
759  | 0  |         ows_request->enabled_layers[ows_request->numlayers] = lp->index;  | 
760  | 0  |         ows_request->numlayers++;  | 
761  | 0  |       }  | 
762  | 0  |     }  | 
763  |  | 
  | 
764  | 0  |     if (ows_request->numlayers == 0) { | 
765  | 0  |       msFree(ows_request->enabled_layers);  | 
766  | 0  |       ows_request->enabled_layers = NULL;  | 
767  | 0  |     }  | 
768  | 0  |   }  | 
769  | 0  | }  | 
770  |  |  | 
771  |  | /* msOWSParseRequestMetadata  | 
772  |  |  *  | 
773  |  |  * This function parse a enable_request metadata string and check if the  | 
774  |  |  * given request is present and enabled.  | 
775  |  |  */  | 
776  |  | int msOWSParseRequestMetadata(const char *metadata, const char *request,  | 
777  | 0  |                               int *disabled) { | 
778  | 0  |   char requestBuffer[32];  | 
779  | 0  |   int wordFlag = MS_FALSE;  | 
780  | 0  |   int disableFlag = MS_FALSE;  | 
781  | 0  |   int allFlag = MS_FALSE;  | 
782  | 0  |   char *bufferPtr, *ptr = NULL;  | 
783  |  | 
  | 
784  | 0  |   *disabled = MS_FALSE;  | 
785  |  | 
  | 
786  | 0  |   if (metadata == NULL)  | 
787  | 0  |     return MS_FALSE;  | 
788  |  |  | 
789  | 0  |   ptr = (char *)metadata;  | 
790  | 0  |   const size_t len = strlen(ptr);  | 
791  | 0  |   requestBuffer[0] = '\0';  | 
792  | 0  |   bufferPtr = requestBuffer;  | 
793  |  | 
  | 
794  | 0  |   for (size_t i = 0; i <= len; ++i, ++ptr) { | 
795  |  | 
  | 
796  | 0  |     if (!wordFlag && isspace(*ptr))  | 
797  | 0  |       continue;  | 
798  |  |  | 
799  | 0  |     wordFlag = MS_TRUE;  | 
800  |  | 
  | 
801  | 0  |     if (*ptr == '!') { | 
802  | 0  |       disableFlag = MS_TRUE;  | 
803  | 0  |       continue;  | 
804  | 0  |     } else if ((*ptr == ' ') ||  | 
805  | 0  |                (*ptr != '\0' && ptr[1] == '\0')) { /* end of word */ | 
806  | 0  |       if (ptr[1] == '\0' && *ptr != ' ') { | 
807  | 0  |         *bufferPtr = *ptr;  | 
808  | 0  |         ++bufferPtr;  | 
809  | 0  |       }  | 
810  |  | 
  | 
811  | 0  |       *bufferPtr = '\0';  | 
812  | 0  |       if (strcasecmp(request, requestBuffer) == 0) { | 
813  | 0  |         *disabled = MS_TRUE; /* explicitly found, will stop the process in  | 
814  |  |                                 msOWSRequestIsEnabled() */  | 
815  | 0  |         return (disableFlag ? MS_FALSE : MS_TRUE);  | 
816  | 0  |       } else { | 
817  | 0  |         if (strcmp("*", requestBuffer) == | 
818  | 0  |             0) { /* check if we read the all flag */ | 
819  | 0  |           if (disableFlag)  | 
820  | 0  |             *disabled = MS_TRUE;  | 
821  | 0  |           allFlag = disableFlag ? MS_FALSE : MS_TRUE;  | 
822  | 0  |         }  | 
823  |  |         /* reset flags */  | 
824  | 0  |         wordFlag = MS_FALSE;  | 
825  | 0  |         disableFlag = MS_FALSE;  | 
826  | 0  |         bufferPtr = requestBuffer;  | 
827  | 0  |       }  | 
828  | 0  |     } else { | 
829  | 0  |       *bufferPtr = *ptr;  | 
830  | 0  |       ++bufferPtr;  | 
831  | 0  |     }  | 
832  | 0  |   }  | 
833  |  |  | 
834  | 0  |   return allFlag;  | 
835  | 0  | }  | 
836  |  |  | 
837  |  | /*  | 
838  |  | ** msOWSGetPrefixFromNamespace()  | 
839  |  | **  | 
840  |  | ** Return the metadata name prefix from a character identifying the OWS  | 
841  |  | ** namespace.  | 
842  |  | */  | 
843  | 0  | static const char *msOWSGetPrefixFromNamespace(char chNamespace) { | 
844  |  |   // Return should be a 3 character string, otherwise breaks assumption  | 
845  |  |   // in msOWSLookupMetadata()  | 
846  | 0  |   switch (chNamespace) { | 
847  | 0  |   case 'O':  | 
848  | 0  |     return "ows";  | 
849  | 0  |   case 'A':  | 
850  | 0  |     return "oga"; /* oga_... (OGC Geospatial API) */  | 
851  | 0  |   case 'M':  | 
852  | 0  |     return "wms";  | 
853  | 0  |   case 'F':  | 
854  | 0  |     return "wfs";  | 
855  | 0  |   case 'C':  | 
856  | 0  |     return "wcs";  | 
857  | 0  |   case 'G':  | 
858  | 0  |     return "gml";  | 
859  | 0  |   case 'S':  | 
860  | 0  |     return "sos";  | 
861  | 0  |   default:  | 
862  |  |     /* We should never get here unless an invalid code (typo) is */  | 
863  |  |     /* present in the code, but since this happened before... */  | 
864  | 0  |     msSetError(MS_WMSERR, "Unsupported metadata namespace code (%c).",  | 
865  | 0  |                "msOWSGetPrefixFromNamespace()", chNamespace);  | 
866  | 0  |     assert(MS_FALSE);  | 
867  | 0  |     return NULL;  | 
868  | 0  |   }  | 
869  | 0  | }  | 
870  |  |  | 
871  |  | /*  | 
872  |  | ** msOWSLookupMetadata()  | 
873  |  | **  | 
874  |  | ** Attempts to lookup a given metadata name in multiple OWS namespaces.  | 
875  |  | **  | 
876  |  | ** 'namespaces' is a string with a letter for each namespace to lookup  | 
877  |  | ** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_  | 
878  |  | ** If namespaces is NULL then this function just does a regular metadata  | 
879  |  | ** lookup.  | 
880  |  | */  | 
881  |  | const char *msOWSLookupMetadata(hashTableObj *metadata, const char *namespaces,  | 
882  | 0  |                                 const char *name) { | 
883  | 0  |   const char *value = NULL;  | 
884  |  | 
  | 
885  | 0  |   if (namespaces == NULL) { | 
886  | 0  |     value = msLookupHashTable(metadata, (char *)name);  | 
887  | 0  |   } else { | 
888  | 0  |     char buf[100] = "ows_";  | 
889  |  | 
  | 
890  | 0  |     strlcpy(buf + 4, name, 96);  | 
891  |  | 
  | 
892  | 0  |     while (value == NULL && *namespaces != '\0') { | 
893  | 0  |       const char *prefix = msOWSGetPrefixFromNamespace(*namespaces);  | 
894  | 0  |       if (prefix == NULL)  | 
895  | 0  |         return NULL;  | 
896  | 0  |       assert(strlen(prefix) == 3);  | 
897  | 0  |       memcpy(buf, prefix, 3);  | 
898  | 0  |       value = msLookupHashTable(metadata, buf);  | 
899  | 0  |       namespaces++;  | 
900  | 0  |     }  | 
901  | 0  |   }  | 
902  |  |  | 
903  | 0  |   return value;  | 
904  | 0  | }  | 
905  |  |  | 
906  |  | /*  | 
907  |  | ** msOWSLookupMetadataWithLanguage()  | 
908  |  | **  | 
909  |  | ** Attempts to lookup a given metadata name in multiple OWS namespaces  | 
910  |  | ** for a specific language.  | 
911  |  | */  | 
912  |  | const char *msOWSLookupMetadataWithLanguage(hashTableObj *metadata,  | 
913  |  |                                             const char *namespaces,  | 
914  |  |                                             const char *name,  | 
915  | 0  |                                             const char *validated_language) { | 
916  | 0  |   const char *value = NULL;  | 
917  |  | 
  | 
918  | 0  |   if (name && validated_language && validated_language[0]) { | 
919  | 0  |     size_t bufferSize = strlen(name) + strlen(validated_language) + 2;  | 
920  | 0  |     char *name2 = (char *)msSmallMalloc(bufferSize);  | 
921  | 0  |     snprintf(name2, bufferSize, "%s.%s", name, validated_language);  | 
922  | 0  |     value = msOWSLookupMetadata(metadata, namespaces, name2);  | 
923  | 0  |     free(name2);  | 
924  | 0  |   }  | 
925  |  | 
  | 
926  | 0  |   if (name && !value) { | 
927  | 0  |     value = msOWSLookupMetadata(metadata, namespaces, name);  | 
928  | 0  |   }  | 
929  |  | 
  | 
930  | 0  |   return value;  | 
931  | 0  | }  | 
932  |  |  | 
933  |  | /*  | 
934  |  | ** msOWSLookupMetadata2()  | 
935  |  | **  | 
936  |  | ** Attempts to lookup a given metadata name in multiple hashTables, and  | 
937  |  | ** in multiple OWS namespaces within each. First searches the primary  | 
938  |  | ** table and if no result is found, attempts the search using the  | 
939  |  | ** secondary (fallback) table.  | 
940  |  | **  | 
941  |  | ** 'namespaces' is a string with a letter for each namespace to lookup  | 
942  |  | ** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_  | 
943  |  | ** If namespaces is NULL then this function just does a regular metadata  | 
944  |  | ** lookup.  | 
945  |  | */  | 
946  |  | const char *msOWSLookupMetadata2(hashTableObj *pri, hashTableObj *sec,  | 
947  | 0  |                                  const char *namespaces, const char *name) { | 
948  | 0  |   const char *result;  | 
949  |  | 
  | 
950  | 0  |   if ((result = msOWSLookupMetadata(pri, namespaces, name)) == NULL) { | 
951  |  |     /* Try the secondary table */  | 
952  | 0  |     result = msOWSLookupMetadata(sec, namespaces, name);  | 
953  | 0  |   }  | 
954  |  | 
  | 
955  | 0  |   return result;  | 
956  | 0  | }  | 
957  |  |  | 
958  |  | /* msOWSParseVersionString()  | 
959  |  | **  | 
960  |  | ** Parse a version string in the format "a.b.c" or "a.b" and return an  | 
961  |  | ** integer in the format 0x0a0b0c suitable for regular integer comparisons.  | 
962  |  | **  | 
963  |  | ** Returns one of OWS_VERSION_NOTSET or OWS_VERSION_BADFORMAT if version  | 
964  |  | ** could not be parsed.  | 
965  |  | */  | 
966  | 0  | int msOWSParseVersionString(const char *pszVersion) { | 
967  | 0  |   char **digits = NULL;  | 
968  | 0  |   int numDigits = 0;  | 
969  |  | 
  | 
970  | 0  |   if (pszVersion) { | 
971  | 0  |     int nVersion = 0;  | 
972  | 0  |     digits = msStringSplit(pszVersion, '.', &numDigits);  | 
973  | 0  |     if (digits == NULL || numDigits < 2 || numDigits > 3) { | 
974  | 0  |       msSetError(MS_OWSERR,  | 
975  | 0  |                  "Invalid version (%s). Version must be in the "  | 
976  | 0  |                  "format 'x.y' or 'x.y.z'",  | 
977  | 0  |                  "msOWSParseVersionString()", pszVersion);  | 
978  | 0  |       if (digits)  | 
979  | 0  |         msFreeCharArray(digits, numDigits);  | 
980  | 0  |       return OWS_VERSION_BADFORMAT;  | 
981  | 0  |     }  | 
982  |  |  | 
983  | 0  |     nVersion = atoi(digits[0]) * 0x010000;  | 
984  | 0  |     nVersion += atoi(digits[1]) * 0x0100;  | 
985  | 0  |     if (numDigits > 2)  | 
986  | 0  |       nVersion += atoi(digits[2]);  | 
987  |  | 
  | 
988  | 0  |     msFreeCharArray(digits, numDigits);  | 
989  |  | 
  | 
990  | 0  |     return nVersion;  | 
991  | 0  |   }  | 
992  |  |  | 
993  | 0  |   return OWS_VERSION_NOTSET;  | 
994  | 0  | }  | 
995  |  |  | 
996  |  | /* msOWSGetVersionString()  | 
997  |  | **  | 
998  |  | ** Returns a OWS version string in the format a.b.c from the integer  | 
999  |  | ** version value passed as argument (0x0a0b0c)  | 
1000  |  | **  | 
1001  |  | ** Fills in the pszBuffer and returns a reference to it. Recommended buffer  | 
1002  |  | ** size is OWS_VERSION_MAXLEN chars.  | 
1003  |  | */  | 
1004  | 0  | const char *msOWSGetVersionString(int nVersion, char *pszBuffer) { | 
1005  |  | 
  | 
1006  | 0  |   if (pszBuffer)  | 
1007  | 0  |     snprintf(pszBuffer, OWS_VERSION_MAXLEN - 1, "%d.%d.%d",  | 
1008  | 0  |              (nVersion / 0x10000) % 0x100, (nVersion / 0x100) % 0x100,  | 
1009  | 0  |              nVersion % 0x100);  | 
1010  |  | 
  | 
1011  | 0  |   return pszBuffer;  | 
1012  | 0  | }  | 
1013  |  |  | 
1014  |  | /*  | 
1015  |  | ** msOWSGetEPSGProj()  | 
1016  |  | **  | 
1017  |  | ** Extract projection code for this layer/map.  | 
1018  |  | **  | 
1019  |  | ** First look for a xxx_srs metadata. If not found then look for an EPSG  | 
1020  |  | ** code in projectionObj, and if not found then return NULL.  | 
1021  |  | **  | 
1022  |  | ** If bReturnOnlyFirstOne=TRUE and metadata contains multiple EPSG codes  | 
1023  |  | ** then only the first one (which is assumed to be the layer's default  | 
1024  |  | ** projection) is returned.  | 
1025  |  | */  | 
1026  |  | void msOWSGetEPSGProj(projectionObj *proj, hashTableObj *metadata,  | 
1027  |  |                       const char *namespaces, int bReturnOnlyFirstOne,  | 
1028  | 0  |                       char **epsgCode) { | 
1029  | 0  |   const char *value;  | 
1030  | 0  |   *epsgCode = NULL;  | 
1031  |  |  | 
1032  |  |   /* metadata value should already be in format "EPSG:n" or "AUTO:..." */  | 
1033  | 0  |   if (metadata &&  | 
1034  | 0  |       ((value = msOWSLookupMetadata(metadata, namespaces, "srs")) != NULL)) { | 
1035  | 0  |     const char *space_ptr;  | 
1036  | 0  |     if (!bReturnOnlyFirstOne || (space_ptr = strchr(value, ' ')) == NULL) { | 
1037  | 0  |       *epsgCode = msStrdup(value);  | 
1038  | 0  |       return;  | 
1039  | 0  |     }  | 
1040  |  |  | 
1041  | 0  |     *epsgCode = msSmallMalloc((space_ptr - value + 1) * sizeof(char));  | 
1042  |  |     /* caller requested only first projection code, copy up to the first space  | 
1043  |  |      * character*/  | 
1044  | 0  |     strlcpy(*epsgCode, value, space_ptr - value + 1);  | 
1045  | 0  |     return;  | 
1046  | 0  |   } else if (proj && proj->numargs > 0 &&  | 
1047  | 0  |              (value = strstr(proj->args[0], "init=epsg:")) != NULL) { | 
1048  | 0  |     *epsgCode = msSmallMalloc((strlen("EPSG:") + strlen(value + 10) + 1) * | 
1049  | 0  |                               sizeof(char));  | 
1050  | 0  |     sprintf(*epsgCode, "EPSG:%s", value + 10);  | 
1051  | 0  |     return;  | 
1052  | 0  |   } else if (proj && proj->numargs > 0 &&  | 
1053  | 0  |              (value = strstr(proj->args[0], "init=crs:")) != NULL) { | 
1054  | 0  |     *epsgCode =  | 
1055  | 0  |         msSmallMalloc((strlen("CRS:") + strlen(value + 9) + 1) * sizeof(char)); | 
1056  | 0  |     sprintf(*epsgCode, "CRS:%s", value + 9);  | 
1057  | 0  |     return;  | 
1058  | 0  |   } else if (proj && proj->numargs > 0 &&  | 
1059  | 0  |              (strncasecmp(proj->args[0], "AUTO:", 5) == 0 ||  | 
1060  | 0  |               strncasecmp(proj->args[0], "AUTO2:", 6) == 0)) { | 
1061  | 0  |     *epsgCode = msStrdup(proj->args[0]);  | 
1062  | 0  |     return;  | 
1063  | 0  |   }  | 
1064  | 0  | }  | 
1065  |  |  | 
1066  |  | /*  | 
1067  |  | ** msOWSProjectToWGS84()  | 
1068  |  | **  | 
1069  |  | ** Reprojects the extent to WGS84.  | 
1070  |  | **  | 
1071  |  | */  | 
1072  | 0  | void msOWSProjectToWGS84(projectionObj *srcproj, rectObj *ext) { | 
1073  | 0  |   if (srcproj->proj && !msProjIsGeographicCRS(srcproj)) { | 
1074  | 0  |     projectionObj wgs84;  | 
1075  | 0  |     msInitProjection(&wgs84);  | 
1076  | 0  |     msProjectionInheritContextFrom(&wgs84, srcproj);  | 
1077  | 0  |     msLoadProjectionString(&wgs84, "+proj=longlat +ellps=WGS84 +datum=WGS84");  | 
1078  | 0  |     msProjectRect(srcproj, &wgs84, ext);  | 
1079  | 0  |     msFreeProjection(&wgs84);  | 
1080  | 0  |   }  | 
1081  | 0  | }  | 
1082  |  |  | 
1083  |  | /* msOWSGetLanguage()  | 
1084  |  | **  | 
1085  |  | ** returns the language via MAP/WEB/METADATA/ows_language  | 
1086  |  | **  | 
1087  |  | ** Use value of "ows_language" metadata, if not set then  | 
1088  |  | ** return "undefined" as a default  | 
1089  |  | */  | 
1090  | 0  | const char *msOWSGetLanguage(mapObj *map, const char *context) { | 
1091  | 0  |   const char *language;  | 
1092  |  |  | 
1093  |  |   /* if this is an exception, MapServer always returns Exception  | 
1094  |  |      messages in en-US  | 
1095  |  |   */  | 
1096  | 0  |   if (strcmp(context, "exception") == 0) { | 
1097  | 0  |     language = MS_ERROR_LANGUAGE;  | 
1098  | 0  |   }  | 
1099  |  |   /* if not, fetch language from mapfile metadata */  | 
1100  | 0  |   else { | 
1101  | 0  |     language = msLookupHashTable(&(map->web.metadata), "ows_language");  | 
1102  |  | 
  | 
1103  | 0  |     if (language == NULL) { | 
1104  | 0  |       language = "undefined";  | 
1105  | 0  |     }  | 
1106  | 0  |   }  | 
1107  | 0  |   return language;  | 
1108  | 0  | }  | 
1109  |  |  | 
1110  |  | /* msOWSGetSchemasLocation()  | 
1111  |  | **  | 
1112  |  | ** schemas location is the root of the web tree where all WFS-related  | 
1113  |  | ** schemas can be found on this server.  These URLs must exist in order  | 
1114  |  | ** to validate xml.  | 
1115  |  | **  | 
1116  |  | ** Use value of "ows_schemas_location" metadata, if not set then  | 
1117  |  | ** return ".." as a default  | 
1118  |  | */  | 
1119  | 0  | const char *msOWSGetSchemasLocation(mapObj *map) { | 
1120  | 0  |   const char *schemas_location;  | 
1121  |  | 
  | 
1122  | 0  |   schemas_location =  | 
1123  | 0  |       msLookupHashTable(&(map->web.metadata), "ows_schemas_location");  | 
1124  | 0  |   if (schemas_location == NULL)  | 
1125  | 0  |     schemas_location = OWS_DEFAULT_SCHEMAS_LOCATION;  | 
1126  |  | 
  | 
1127  | 0  |   return schemas_location;  | 
1128  | 0  | }  | 
1129  |  |  | 
1130  |  | /*  | 
1131  |  | ** msOWSGetExpandedMetadataKey()  | 
1132  |  | */  | 
1133  |  | static char *msOWSGetExpandedMetadataKey(const char *namespaces,  | 
1134  | 0  |                                          const char *metadata_name) { | 
1135  | 0  |   char *pszRet = msStringConcatenate(NULL, "");  | 
1136  | 0  |   for (int i = 0; namespaces[i] != '\0'; ++i) { | 
1137  | 0  |     if (i > 0)  | 
1138  | 0  |       pszRet = msStringConcatenate(pszRet, " or ");  | 
1139  | 0  |     pszRet = msStringConcatenate(pszRet, "\"");  | 
1140  | 0  |     pszRet =  | 
1141  | 0  |         msStringConcatenate(pszRet, msOWSGetPrefixFromNamespace(namespaces[i]));  | 
1142  | 0  |     pszRet = msStringConcatenate(pszRet, "_");  | 
1143  | 0  |     pszRet = msStringConcatenate(pszRet, metadata_name);  | 
1144  | 0  |     pszRet = msStringConcatenate(pszRet, "\"");  | 
1145  | 0  |   }  | 
1146  | 0  |   return pszRet;  | 
1147  | 0  | }  | 
1148  |  |  | 
1149  |  | /*  | 
1150  |  | ** msOWSGetOnlineResource()  | 
1151  |  | **  | 
1152  |  | ** Return the online resource for this service.  First try to lookup  | 
1153  |  | ** specified metadata, and if not found then try to build the URL ourselves.  | 
1154  |  | **  | 
1155  |  | ** Returns a newly allocated string that should be freed by the caller or  | 
1156  |  | ** NULL in case of error.  | 
1157  |  | */  | 
1158  |  | char *msOWSGetOnlineResource(mapObj *map, const char *namespaces,  | 
1159  | 0  |                              const char *metadata_name, cgiRequestObj *req) { | 
1160  | 0  |   const char *value;  | 
1161  | 0  |   char *online_resource = NULL;  | 
1162  |  |  | 
1163  |  |   /* We need this script's URL, including hostname. */  | 
1164  |  |   /* Default to use the value of the "onlineresource" metadata, and if not */  | 
1165  |  |   /* set then build it: "http://$(SERVER_NAME):$(SERVER_PORT)$(SCRIPT_NAME)?" */  | 
1166  |  |   /* (+append the map=... param if it was explicitly passed in QUERY_STRING) */  | 
1167  |  |   /*  */  | 
1168  | 0  |   if ((value = msOWSLookupMetadata(&(map->web.metadata), namespaces,  | 
1169  | 0  |                                    metadata_name))) { | 
1170  | 0  |     online_resource = msOWSTerminateOnlineResource(value);  | 
1171  | 0  |   } else { | 
1172  | 0  |     if ((online_resource = msBuildOnlineResource(map, req)) == NULL) { | 
1173  | 0  |       char *pszExpandedMetadataKey =  | 
1174  | 0  |           msOWSGetExpandedMetadataKey(namespaces, metadata_name);  | 
1175  | 0  |       msSetError(MS_CGIERR, "Please set %s metadata.",  | 
1176  | 0  |                  "msOWSGetOnlineResource()", pszExpandedMetadataKey);  | 
1177  | 0  |       msFree(pszExpandedMetadataKey);  | 
1178  | 0  |       return NULL;  | 
1179  | 0  |     }  | 
1180  | 0  |   }  | 
1181  |  |  | 
1182  | 0  |   return online_resource;  | 
1183  | 0  | }  | 
1184  |  |  | 
1185  |  | /*  | 
1186  |  | ** msOWSTerminateOnlineResource()  | 
1187  |  | **  | 
1188  |  | ** Append trailing "?" or "&" to an onlineresource URL if it doesn't have  | 
1189  |  | ** one already. The returned string is then ready to append GET parameters  | 
1190  |  | ** to it.  | 
1191  |  | **  | 
1192  |  | ** Returns a newly allocated string that should be freed by the caller or  | 
1193  |  | ** NULL in case of error.  | 
1194  |  | */  | 
1195  | 0  | char *msOWSTerminateOnlineResource(const char *src_url) { | 
1196  | 0  |   char *online_resource = NULL;  | 
1197  | 0  |   size_t buffer_size = 0;  | 
1198  |  | 
  | 
1199  | 0  |   if (src_url == NULL)  | 
1200  | 0  |     return NULL;  | 
1201  |  |  | 
1202  | 0  |   buffer_size = strlen(src_url) + 2;  | 
1203  | 0  |   online_resource = (char *)malloc(buffer_size);  | 
1204  |  | 
  | 
1205  | 0  |   if (online_resource == NULL) { | 
1206  | 0  |     msSetError(MS_MEMERR, NULL, "msOWSTerminateOnlineResource()");  | 
1207  | 0  |     return NULL;  | 
1208  | 0  |   }  | 
1209  |  |  | 
1210  | 0  |   strlcpy(online_resource, src_url, buffer_size);  | 
1211  |  |  | 
1212  |  |   /* Append trailing '?' or '&' if missing. */  | 
1213  | 0  |   if (strchr(online_resource, '?') == NULL)  | 
1214  | 0  |     strlcat(online_resource, "?", buffer_size);  | 
1215  | 0  |   else { | 
1216  | 0  |     char *c;  | 
1217  | 0  |     c = online_resource + strlen(online_resource) - 1;  | 
1218  | 0  |     if (*c != '?' && *c != '&')  | 
1219  | 0  |       strlcpy(c + 1, "&", buffer_size - strlen(online_resource));  | 
1220  | 0  |   }  | 
1221  |  | 
  | 
1222  | 0  |   return online_resource;  | 
1223  | 0  | }  | 
1224  |  |  | 
1225  |  | /************************************************************************/  | 
1226  |  | /*                         msUpdateGMLFieldMetadata                     */  | 
1227  |  | /*                                                                      */  | 
1228  |  | /*      Updates a fields GML metadata if it has not already             */  | 
1229  |  | /*      been set. Nullable is not implemented for all drivers           */  | 
1230  |  | /*      and can be set to 0 if unknown                                  */  | 
1231  |  | /************************************************************************/  | 
1232  |  | int msUpdateGMLFieldMetadata(layerObj *layer, const char *field_name,  | 
1233  |  |                              const char *gml_type, const char *gml_width,  | 
1234  | 0  |                              const char *gml_precision, const short nullable) { | 
1235  |  | 
  | 
1236  | 0  |   char md_item_name[256];  | 
1237  |  | 
  | 
1238  | 0  |   snprintf(md_item_name, sizeof(md_item_name), "gml_%s_type", field_name);  | 
1239  | 0  |   if (msLookupHashTable(&(layer->metadata), md_item_name) == NULL)  | 
1240  | 0  |     msInsertHashTable(&(layer->metadata), md_item_name, gml_type);  | 
1241  |  | 
  | 
1242  | 0  |   snprintf(md_item_name, sizeof(md_item_name), "gml_%s_width", field_name);  | 
1243  | 0  |   if (strlen(gml_width) > 0 &&  | 
1244  | 0  |       msLookupHashTable(&(layer->metadata), md_item_name) == NULL)  | 
1245  | 0  |     msInsertHashTable(&(layer->metadata), md_item_name, gml_width);  | 
1246  |  | 
  | 
1247  | 0  |   snprintf(md_item_name, sizeof(md_item_name), "gml_%s_precision", field_name);  | 
1248  | 0  |   if (strlen(gml_precision) > 0 &&  | 
1249  | 0  |       msLookupHashTable(&(layer->metadata), md_item_name) == NULL)  | 
1250  | 0  |     msInsertHashTable(&(layer->metadata), md_item_name, gml_precision);  | 
1251  |  | 
  | 
1252  | 0  |   snprintf(md_item_name, sizeof(md_item_name), "gml_%s_nillable", field_name);  | 
1253  | 0  |   if (nullable > 0 &&  | 
1254  | 0  |       msLookupHashTable(&(layer->metadata), md_item_name) == NULL)  | 
1255  | 0  |     msInsertHashTable(&(layer->metadata), md_item_name, "true");  | 
1256  |  | 
  | 
1257  | 0  |   return MS_TRUE;  | 
1258  | 0  | }  | 
1259  |  |  | 
1260  |  | #if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \  | 
1261  |  |     defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)  | 
1262  |  |  | 
1263  |  | /*  | 
1264  |  | ** msRenameLayer()  | 
1265  |  | */  | 
1266  | 0  | static int msRenameLayer(layerObj *lp, int count) { | 
1267  | 0  |   char *newname;  | 
1268  | 0  |   newname = (char *)malloc((strlen(lp->name) + 5) * sizeof(char));  | 
1269  | 0  |   if (!newname) { | 
1270  | 0  |     msSetError(MS_MEMERR, NULL, "msRenameLayer()");  | 
1271  | 0  |     return MS_FAILURE;  | 
1272  | 0  |   }  | 
1273  | 0  |   sprintf(newname, "%s_%2.2d", lp->name, count);  | 
1274  | 0  |   free(lp->name);  | 
1275  | 0  |   lp->name = newname;  | 
1276  |  | 
  | 
1277  | 0  |   return MS_SUCCESS;  | 
1278  | 0  | }  | 
1279  |  |  | 
1280  |  | /*  | 
1281  |  | ** msOWSMakeAllLayersUnique()  | 
1282  |  | */  | 
1283  | 0  | int msOWSMakeAllLayersUnique(mapObj *map) { | 
1284  | 0  |   int i, j;  | 
1285  |  |  | 
1286  |  |   /* Make sure all layers in the map file have valid and unique names */  | 
1287  | 0  |   for (i = 0; i < map->numlayers; i++) { | 
1288  | 0  |     int count = 1;  | 
1289  | 0  |     for (j = i + 1; j < map->numlayers; j++) { | 
1290  | 0  |       if (GET_LAYER(map, i)->name == NULL || GET_LAYER(map, j)->name == NULL) { | 
1291  | 0  |         continue;  | 
1292  | 0  |       }  | 
1293  | 0  |       if (strcasecmp(GET_LAYER(map, i)->name, GET_LAYER(map, j)->name) == 0 &&  | 
1294  | 0  |           msRenameLayer((GET_LAYER(map, j)), ++count) != MS_SUCCESS) { | 
1295  | 0  |         return MS_FAILURE;  | 
1296  | 0  |       }  | 
1297  | 0  |     }  | 
1298  |  |  | 
1299  |  |     /* Don't forget to rename the first layer if duplicates were found */  | 
1300  | 0  |     if (count > 1 && msRenameLayer((GET_LAYER(map, i)), 1) != MS_SUCCESS) { | 
1301  | 0  |       return MS_FAILURE;  | 
1302  | 0  |     }  | 
1303  | 0  |   }  | 
1304  | 0  |   return MS_SUCCESS;  | 
1305  | 0  | }  | 
1306  |  |  | 
1307  |  | /*  | 
1308  |  | ** msOWSNegotiateVersion()  | 
1309  |  | **  | 
1310  |  | ** returns the most suitable version an OWS is to support given a client  | 
1311  |  | ** version parameter.  | 
1312  |  | **  | 
1313  |  | ** supported_versions must be ordered from highest to lowest  | 
1314  |  | **  | 
1315  |  | ** Returns a version integer of the supported version  | 
1316  |  | **  | 
1317  |  | */  | 
1318  |  |  | 
1319  |  | int msOWSNegotiateVersion(int requested_version, const int supported_versions[],  | 
1320  | 0  |                           int num_supported_versions) { | 
1321  | 0  |   int i;  | 
1322  |  |  | 
1323  |  |   /* if version is not set return highest version */  | 
1324  | 0  |   if (!requested_version)  | 
1325  | 0  |     return supported_versions[0];  | 
1326  |  |  | 
1327  |  |   /* if the requested version is lower than the lowest version return the lowest  | 
1328  |  |    * version  */  | 
1329  | 0  |   if (requested_version < supported_versions[num_supported_versions - 1])  | 
1330  | 0  |     return supported_versions[num_supported_versions - 1];  | 
1331  |  |  | 
1332  |  |   /* return the first entry that's lower than or equal to the requested version  | 
1333  |  |    */  | 
1334  | 0  |   for (i = 0; i < num_supported_versions; i++) { | 
1335  | 0  |     if (supported_versions[i] <= requested_version)  | 
1336  | 0  |       return supported_versions[i];  | 
1337  | 0  |   }  | 
1338  |  |  | 
1339  | 0  |   return requested_version;  | 
1340  | 0  | }  | 
1341  |  |  | 
1342  |  | /*  | 
1343  |  | ** msOWSGetOnlineResource()  | 
1344  |  | **  | 
1345  |  | ** Return the online resource for this service and add language parameter.  | 
1346  |  | **  | 
1347  |  | ** Returns a newly allocated string that should be freed by the caller or  | 
1348  |  | ** NULL in case of error.  | 
1349  |  | */  | 
1350  |  | char *msOWSGetOnlineResource2(mapObj *map, const char *namespaces,  | 
1351  |  |                               const char *metadata_name, cgiRequestObj *req,  | 
1352  | 0  |                               const char *validated_language) { | 
1353  | 0  |   char *online_resource =  | 
1354  | 0  |       msOWSGetOnlineResource(map, namespaces, metadata_name, req);  | 
1355  |  | 
  | 
1356  | 0  |   if (online_resource && validated_language && validated_language[0]) { | 
1357  |  |     /* online_resource is already terminated, so we can simply add language=...&  | 
1358  |  |      */  | 
1359  |  |     /* but first we need to make sure that online_resource has enough capacity  | 
1360  |  |      */  | 
1361  | 0  |     online_resource = (char *)msSmallRealloc(  | 
1362  | 0  |         online_resource,  | 
1363  | 0  |         strlen(online_resource) + strlen(validated_language) + 11);  | 
1364  | 0  |     strcat(online_resource, "language=");  | 
1365  | 0  |     strcat(online_resource, validated_language);  | 
1366  | 0  |     strcat(online_resource, "&");  | 
1367  | 0  |   }  | 
1368  |  | 
  | 
1369  | 0  |   return online_resource;  | 
1370  | 0  | }  | 
1371  |  |  | 
1372  |  | /* msOWSGetInspireSchemasLocation()  | 
1373  |  | **  | 
1374  |  | ** schemas location is the root of the web tree where all Inspire-related  | 
1375  |  | ** schemas can be found on this server.  These URLs must exist in order  | 
1376  |  | ** to validate xml.  | 
1377  |  | **  | 
1378  |  | ** Use value of "inspire_schemas_location" metadata  | 
1379  |  | */  | 
1380  | 0  | const char *msOWSGetInspireSchemasLocation(mapObj *map) { | 
1381  | 0  |   const char *schemas_location;  | 
1382  |  | 
  | 
1383  | 0  |   schemas_location =  | 
1384  | 0  |       msLookupHashTable(&(map->web.metadata), "inspire_schemas_location");  | 
1385  | 0  |   if (schemas_location == NULL)  | 
1386  | 0  |     schemas_location = "http://inspire.ec.europa.eu/schemas";  | 
1387  |  | 
  | 
1388  | 0  |   return schemas_location;  | 
1389  | 0  | }  | 
1390  |  |  | 
1391  |  | /* msOWSGetLanguageList  | 
1392  |  | **  | 
1393  |  | ** Returns the list of languages that this service supports  | 
1394  |  | **  | 
1395  |  | ** Use value of "languages" metadata (comma-separated list), or NULL if not set  | 
1396  |  | **  | 
1397  |  | ** Returns a malloced char** of length numitems which must be freed  | 
1398  |  | ** by the caller, or NULL (with numitems = 0)  | 
1399  |  | */  | 
1400  |  | char **msOWSGetLanguageList(mapObj *map, const char *namespaces,  | 
1401  | 0  |                             int *numitems) { | 
1402  |  | 
  | 
1403  | 0  |   const char *languages = NULL;  | 
1404  |  | 
  | 
1405  | 0  |   languages =  | 
1406  | 0  |       msOWSLookupMetadata(&(map->web.metadata), namespaces, "languages");  | 
1407  | 0  |   if (languages && strlen(languages) > 0) { | 
1408  | 0  |     return msStringSplit(languages, ',', numitems);  | 
1409  | 0  |   } else { | 
1410  | 0  |     *numitems = 0;  | 
1411  | 0  |     return NULL;  | 
1412  | 0  |   }  | 
1413  | 0  | }  | 
1414  |  |  | 
1415  |  | /* msOWSGetLanguageFromList  | 
1416  |  | **  | 
1417  |  | ** Returns a language according to the language requested by the client  | 
1418  |  | **  | 
1419  |  | ** If the requested language is in the languages metadata then use it,  | 
1420  |  | ** otherwise ignore it and use the default language, which is the first entry in  | 
1421  |  | ** the languages metadata list. Calling with a NULL requested_langauge  | 
1422  |  | ** therefore returns this default language. If the language metadata list is  | 
1423  |  | ** not defined then the language is set to NULL.  | 
1424  |  | **  | 
1425  |  | ** Returns a malloced char* which must be freed by the caller, or NULL  | 
1426  |  | */  | 
1427  |  | char *msOWSGetLanguageFromList(mapObj *map, const char *namespaces,  | 
1428  | 0  |                                const char *requested_language) { | 
1429  | 0  |   int num_items = 0;  | 
1430  | 0  |   char **languages = msOWSGetLanguageList(map, namespaces, &num_items);  | 
1431  | 0  |   char *language = NULL;  | 
1432  |  | 
  | 
1433  | 0  |   if (languages && num_items > 0) { | 
1434  | 0  |     if (!requested_language ||  | 
1435  | 0  |         !msStringInArray(requested_language, languages, num_items)) { | 
1436  | 0  |       language = msStrdup(languages[0]);  | 
1437  | 0  |     } else { | 
1438  | 0  |       language = msStrdup(requested_language);  | 
1439  | 0  |     }  | 
1440  | 0  |   }  | 
1441  | 0  |   msFreeCharArray(languages, num_items);  | 
1442  |  | 
  | 
1443  | 0  |   return language;  | 
1444  | 0  | }  | 
1445  |  |  | 
1446  |  | /* msOWSLanguageNegotiation  | 
1447  |  | **  | 
1448  |  | ** Returns a language according to the accepted languages requested by the  | 
1449  |  | *client  | 
1450  |  | **  | 
1451  |  | ** Returns a malloced char* which must be freed by the caller, or NULL  | 
1452  |  | */  | 
1453  |  | char *msOWSLanguageNegotiation(mapObj *map, const char *namespaces,  | 
1454  |  |                                char **accept_languages,  | 
1455  | 0  |                                int num_accept_languages) { | 
1456  | 0  |   int num_languages = 0;  | 
1457  | 0  |   char **languages = NULL;  | 
1458  | 0  |   char *result_language = NULL;  | 
1459  |  | 
  | 
1460  | 0  |   languages = msOWSGetLanguageList(map, namespaces, &num_languages);  | 
1461  |  | 
  | 
1462  | 0  |   if (languages && num_languages > 0) { | 
1463  | 0  |     int i;  | 
1464  | 0  |     for (i = 0; i < num_accept_languages; ++i) { | 
1465  | 0  |       const char *accept_language = accept_languages[i];  | 
1466  |  |  | 
1467  |  |       /* '*' means any language */  | 
1468  | 0  |       if (EQUAL(accept_language, "*")) { | 
1469  | 0  |         result_language = msStrdup(languages[0]);  | 
1470  | 0  |         break;  | 
1471  | 0  |       } else if (msStringInArray(accept_language, languages, num_languages)) { | 
1472  | 0  |         result_language = msStrdup(accept_language);  | 
1473  | 0  |         break;  | 
1474  | 0  |       }  | 
1475  | 0  |     }  | 
1476  |  | 
  | 
1477  | 0  |     if (result_language == NULL) { | 
1478  | 0  |       result_language = msStrdup(languages[0]);  | 
1479  | 0  |     }  | 
1480  | 0  |   }  | 
1481  |  | 
  | 
1482  | 0  |   msFreeCharArray(languages, num_languages);  | 
1483  | 0  |   return result_language;  | 
1484  | 0  | }  | 
1485  |  |  | 
1486  |  | /* msOWSPrintInspireCommonExtendedCapabilities  | 
1487  |  | **  | 
1488  |  | ** Output INSPIRE common extended capabilities items to stream  | 
1489  |  | ** The currently supported items are metadata and languages  | 
1490  |  | **  | 
1491  |  | ** tag_name is the name (including ns prefix) of the tag to include the whole  | 
1492  |  | ** extended capabilities block in  | 
1493  |  | **  | 
1494  |  | ** service is currently included for future compatibility when differing  | 
1495  |  | ** extended capabilities elements are included for different service types  | 
1496  |  | **  | 
1497  |  | ** Returns a status code; MS_NOERR if all ok, action_if_not_found otherwise  | 
1498  |  | */  | 
1499  |  | int msOWSPrintInspireCommonExtendedCapabilities(  | 
1500  |  |     FILE *stream, mapObj *map, const char *namespaces, int action_if_not_found,  | 
1501  |  |     const char *tag_name, const char *tag_ns, const char *validated_language,  | 
1502  | 0  |     const OWSServiceType service) { | 
1503  |  | 
  | 
1504  | 0  |   int metadataStatus = 0;  | 
1505  | 0  |   int languageStatus = 0;  | 
1506  |  | 
  | 
1507  | 0  |   if (tag_ns)  | 
1508  | 0  |     msIO_fprintf(stream, "  <%s %s>\n", tag_name, tag_ns);  | 
1509  | 0  |   else  | 
1510  | 0  |     msIO_fprintf(stream, "  <%s>\n", tag_name);  | 
1511  |  | 
  | 
1512  | 0  |   metadataStatus = msOWSPrintInspireCommonMetadata(  | 
1513  | 0  |       stream, map, namespaces, action_if_not_found, service);  | 
1514  | 0  |   languageStatus = msOWSPrintInspireCommonLanguages(  | 
1515  | 0  |       stream, map, namespaces, action_if_not_found, validated_language);  | 
1516  |  | 
  | 
1517  | 0  |   msIO_fprintf(stream, "  </%s>\n", tag_name);  | 
1518  |  | 
  | 
1519  | 0  |   return (metadataStatus != MS_NOERR) ? metadataStatus : languageStatus;  | 
1520  | 0  | }  | 
1521  |  |  | 
1522  |  | /* msOWSPrintInspireCommonMetadata  | 
1523  |  | **  | 
1524  |  | ** Output INSPIRE common metadata items to a stream  | 
1525  |  | **  | 
1526  |  | ** Returns a status code; MS_NOERR if all OK, action_if_not_found otherwise  | 
1527  |  | */  | 
1528  |  | int msOWSPrintInspireCommonMetadata(FILE *stream, mapObj *map,  | 
1529  |  |                                     const char *namespaces,  | 
1530  |  |                                     int action_if_not_found,  | 
1531  | 0  |                                     const OWSServiceType service) { | 
1532  |  | 
  | 
1533  | 0  |   int status = MS_NOERR;  | 
1534  | 0  |   const char *inspire_capabilities = NULL;  | 
1535  |  | 
  | 
1536  | 0  |   inspire_capabilities = msOWSLookupMetadata(&(map->web.metadata), namespaces,  | 
1537  | 0  |                                              "inspire_capabilities");  | 
1538  |  | 
  | 
1539  | 0  |   if (!inspire_capabilities) { | 
1540  | 0  |     if (OWS_WARN == action_if_not_found) { | 
1541  | 0  |       msIO_fprintf(  | 
1542  | 0  |           stream,  | 
1543  | 0  |           "<!-- WARNING: missing metadata entry for 'inspire_capabilities', "  | 
1544  | 0  |           "one of 'embed' and 'url' must be supplied. -->\n");  | 
1545  | 0  |     }  | 
1546  | 0  |     return action_if_not_found;  | 
1547  | 0  |   }  | 
1548  | 0  |   if (strcasecmp("url", inspire_capabilities) == 0) { | 
1549  | 0  |     if (msOWSLookupMetadata(&(map->web.metadata), namespaces,  | 
1550  | 0  |                             "inspire_metadataurl_href") != NULL) { | 
1551  | 0  |       msIO_fprintf(stream,  | 
1552  | 0  |                    "    <inspire_common:MetadataUrl "  | 
1553  | 0  |                    "xsi:type=\"inspire_common:resourceLocatorType\">\n");  | 
1554  | 0  |       msOWSPrintEncodeMetadata(  | 
1555  | 0  |           stream, &(map->web.metadata), namespaces, "inspire_metadataurl_href",  | 
1556  | 0  |           OWS_WARN, "      <inspire_common:URL>%s</inspire_common:URL>\n", "");  | 
1557  | 0  |       msOWSPrintEncodeMetadata(  | 
1558  | 0  |           stream, &(map->web.metadata), namespaces,  | 
1559  | 0  |           "inspire_metadataurl_format", OWS_WARN,  | 
1560  | 0  |           "      <inspire_common:MediaType>%s</inspire_common:MediaType>\n",  | 
1561  | 0  |           "");  | 
1562  | 0  |       msIO_fprintf(stream, "    </inspire_common:MetadataUrl>\n");  | 
1563  | 0  |     } else { | 
1564  | 0  |       status = action_if_not_found;  | 
1565  | 0  |       if (OWS_WARN == action_if_not_found) { | 
1566  | 0  |         char *pszExpandedMetadataKey =  | 
1567  | 0  |             msOWSGetExpandedMetadataKey(namespaces, "inspire_metadataurl_href");  | 
1568  | 0  |         msIO_fprintf(stream,  | 
1569  | 0  |                      "<!-- WARNING: Mandatory metadata %s was missing in this "  | 
1570  | 0  |                      "context. -->\n",  | 
1571  | 0  |                      pszExpandedMetadataKey);  | 
1572  | 0  |         msFree(pszExpandedMetadataKey);  | 
1573  | 0  |       }  | 
1574  | 0  |     }  | 
1575  | 0  |   } else if (strcasecmp("embed", inspire_capabilities) == 0) { | 
1576  | 0  |     msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,  | 
1577  | 0  |                              "inspire_resourcelocator", OWS_WARN,  | 
1578  | 0  |                              "    <inspire_common:ResourceLocator>\n      "  | 
1579  | 0  |                              "<inspire_common:URL>%s</inspire_common:URL>\n    "  | 
1580  | 0  |                              "</inspire_common:ResourceLocator>\n",  | 
1581  | 0  |                              NULL);  | 
1582  | 0  |     msIO_fprintf(  | 
1583  | 0  |         stream,  | 
1584  | 0  |         "    "  | 
1585  | 0  |         "<inspire_common:ResourceType>service</inspire_common:ResourceType>\n");  | 
1586  | 0  |     msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,  | 
1587  | 0  |                              "inspire_temporal_reference", OWS_WARN,  | 
1588  | 0  |                              "    <inspire_common:TemporalReference>\n      "  | 
1589  | 0  |                              "<inspire_common:DateOfLastRevision>%s</"  | 
1590  | 0  |                              "inspire_common:DateOfLastRevision>\n    "  | 
1591  | 0  |                              "</inspire_common:TemporalReference>\n",  | 
1592  | 0  |                              "");  | 
1593  | 0  |     msIO_fprintf(stream, "    <inspire_common:Conformity>\n");  | 
1594  | 0  |     msIO_fprintf(stream, "      <inspire_common:Specification>\n");  | 
1595  | 0  |     msIO_fprintf(stream,  | 
1596  | 0  |                  "        <inspire_common:Title>-</inspire_common:Title>\n");  | 
1597  | 0  |     msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,  | 
1598  | 0  |                              "inspire_temporal_reference", OWS_NOERR,  | 
1599  | 0  |                              "        "  | 
1600  | 0  |                              "<inspire_common:DateOfLastRevision>%s</"  | 
1601  | 0  |                              "inspire_common:DateOfLastRevision>\n",  | 
1602  | 0  |                              "");  | 
1603  | 0  |     msIO_fprintf(stream, "      </inspire_common:Specification>\n");  | 
1604  | 0  |     msIO_fprintf(  | 
1605  | 0  |         stream,  | 
1606  | 0  |         "      <inspire_common:Degree>notEvaluated</inspire_common:Degree>\n");  | 
1607  | 0  |     msIO_fprintf(stream, "    </inspire_common:Conformity>\n");  | 
1608  | 0  |     msIO_fprintf(stream, "    <inspire_common:MetadataPointOfContact>\n");  | 
1609  | 0  |     msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,  | 
1610  | 0  |                              "inspire_mpoc_name", OWS_WARN,  | 
1611  | 0  |                              "      "  | 
1612  | 0  |                              "<inspire_common:OrganisationName>%s</"  | 
1613  | 0  |                              "inspire_common:OrganisationName>\n",  | 
1614  | 0  |                              "");  | 
1615  | 0  |     msOWSPrintEncodeMetadata(  | 
1616  | 0  |         stream, &(map->web.metadata), namespaces, "inspire_mpoc_email",  | 
1617  | 0  |         OWS_WARN,  | 
1618  | 0  |         "      <inspire_common:EmailAddress>%s</inspire_common:EmailAddress>\n",  | 
1619  | 0  |         "");  | 
1620  | 0  |     msIO_fprintf(stream, "    </inspire_common:MetadataPointOfContact>\n");  | 
1621  | 0  |     msOWSPrintEncodeMetadata(  | 
1622  | 0  |         stream, &(map->web.metadata), namespaces, "inspire_metadatadate",  | 
1623  | 0  |         OWS_WARN,  | 
1624  | 0  |         "      <inspire_common:MetadataDate>%s</inspire_common:MetadataDate>\n",  | 
1625  | 0  |         "");  | 
1626  | 0  |     if (service == OWS_WFS || service == OWS_WCS)  | 
1627  | 0  |       msIO_fprintf(stream, "    "  | 
1628  | 0  |                            "<inspire_common:SpatialDataServiceType>download</"  | 
1629  | 0  |                            "inspire_common:SpatialDataServiceType>\n");  | 
1630  | 0  |     else  | 
1631  | 0  |       msIO_fprintf(stream, "    "  | 
1632  | 0  |                            "<inspire_common:SpatialDataServiceType>view</"  | 
1633  | 0  |                            "inspire_common:SpatialDataServiceType>\n");  | 
1634  | 0  |     msOWSPrintEncodeMetadata(  | 
1635  | 0  |         stream, &(map->web.metadata), namespaces, "inspire_keyword", OWS_WARN,  | 
1636  | 0  |         "    <inspire_common:MandatoryKeyword>\n      "  | 
1637  | 0  |         "<inspire_common:KeywordValue>%s</inspire_common:KeywordValue>\n    "  | 
1638  | 0  |         "</inspire_common:MandatoryKeyword>\n",  | 
1639  | 0  |         "");  | 
1640  | 0  |   } else { | 
1641  | 0  |     status = action_if_not_found;  | 
1642  | 0  |     if (OWS_WARN == action_if_not_found) { | 
1643  | 0  |       msIO_fprintf(  | 
1644  | 0  |           stream,  | 
1645  | 0  |           "<!-- WARNING: invalid value '%s' for 'inspire_capabilities', only "  | 
1646  | 0  |           "'embed' and 'url' are supported. -->\n",  | 
1647  | 0  |           inspire_capabilities);  | 
1648  | 0  |     }  | 
1649  | 0  |   }  | 
1650  |  | 
  | 
1651  | 0  |   return status;  | 
1652  | 0  | }  | 
1653  |  |  | 
1654  |  | /* msOWSPrintInspireCommonLanguages  | 
1655  |  | **  | 
1656  |  | ** Output INSPIRE supported languages block to stream  | 
1657  |  | **  | 
1658  |  | ** Returns a status code; MS_NOERR if all OK; action_if_not_found otherwise  | 
1659  |  | */  | 
1660  |  | int msOWSPrintInspireCommonLanguages(FILE *stream, mapObj *map,  | 
1661  |  |                                      const char *namespaces,  | 
1662  |  |                                      int action_if_not_found,  | 
1663  | 0  |                                      const char *validated_language) { | 
1664  | 0  |   char *buffer =  | 
1665  | 0  |       NULL; /* temp variable for malloced strings that will need freeing */  | 
1666  | 0  |   int status = MS_NOERR;  | 
1667  |  | 
  | 
1668  | 0  |   char *default_language = msOWSGetLanguageFromList(map, namespaces, NULL);  | 
1669  |  | 
  | 
1670  | 0  |   if (validated_language && validated_language[0] && default_language) { | 
1671  | 0  |     msIO_fprintf(stream, "    <inspire_common:SupportedLanguages>\n");  | 
1672  | 0  |     msIO_fprintf(  | 
1673  | 0  |         stream,  | 
1674  | 0  |         "      <inspire_common:DefaultLanguage><inspire_common:Language>%s"  | 
1675  | 0  |         "</inspire_common:Language></inspire_common:DefaultLanguage>\n",  | 
1676  | 0  |         buffer = msEncodeHTMLEntities(default_language));  | 
1677  | 0  |     msFree(buffer);  | 
1678  |  |  | 
1679  |  |     /* append _exclude to our default_language*/  | 
1680  | 0  |     default_language = msSmallRealloc(  | 
1681  | 0  |         default_language, strlen(default_language) + strlen("_exclude") + 1); | 
1682  | 0  |     strcat(default_language, "_exclude");  | 
1683  |  | 
  | 
1684  | 0  |     msOWSPrintEncodeMetadataList(  | 
1685  | 0  |         stream, &(map->web.metadata), namespaces, "languages", NULL, NULL,  | 
1686  | 0  |         "      <inspire_common:SupportedLanguage><inspire_common:Language>%s"  | 
1687  | 0  |         "</inspire_common:Language></inspire_common:SupportedLanguage>\n",  | 
1688  | 0  |         default_language);  | 
1689  | 0  |     msIO_fprintf(stream, "    </inspire_common:SupportedLanguages>\n");  | 
1690  | 0  |     msIO_fprintf(  | 
1691  | 0  |         stream,  | 
1692  | 0  |         "    <inspire_common:ResponseLanguage><inspire_common:Language>%s"  | 
1693  | 0  |         "</inspire_common:Language></inspire_common:ResponseLanguage>\n",  | 
1694  | 0  |         validated_language);  | 
1695  | 0  |   } else { | 
1696  | 0  |     status = action_if_not_found;  | 
1697  | 0  |     if (OWS_WARN == action_if_not_found) { | 
1698  | 0  |       char *pszExpandedMetadataKey =  | 
1699  | 0  |           msOWSGetExpandedMetadataKey(namespaces, "languages");  | 
1700  | 0  |       msIO_fprintf(stream,  | 
1701  | 0  |                    "<!-- WARNING: Mandatory metadata %s was missing in this "  | 
1702  | 0  |                    "context. -->\n",  | 
1703  | 0  |                    pszExpandedMetadataKey);  | 
1704  | 0  |       msFree(pszExpandedMetadataKey);  | 
1705  | 0  |     }  | 
1706  | 0  |   }  | 
1707  |  | 
  | 
1708  | 0  |   msFree(default_language);  | 
1709  |  | 
  | 
1710  | 0  |   return status;  | 
1711  | 0  | }  | 
1712  |  |  | 
1713  |  | /*  | 
1714  |  | ** msOWSPrintMetadata()  | 
1715  |  | **  | 
1716  |  | ** Attempt to output a capability item.  If corresponding metadata is not  | 
1717  |  | ** found then one of a number of predefined actions will be taken.  | 
1718  |  | ** If a default value is provided and metadata is absent then the  | 
1719  |  | ** default will be used.  | 
1720  |  | */  | 
1721  |  |  | 
1722  |  | int msOWSPrintMetadata(FILE *stream, hashTableObj *metadata,  | 
1723  |  |                        const char *namespaces, const char *name,  | 
1724  |  |                        int action_if_not_found, const char *format,  | 
1725  | 0  |                        const char *default_value) { | 
1726  | 0  |   const char *value = NULL;  | 
1727  | 0  |   int status = MS_NOERR;  | 
1728  |  | 
  | 
1729  | 0  |   if ((value = msOWSLookupMetadata(metadata, namespaces, name)) != NULL) { | 
1730  | 0  |     msIO_fprintf(stream, format, value);  | 
1731  | 0  |   } else { | 
1732  | 0  |     if (action_if_not_found == OWS_WARN) { | 
1733  | 0  |       char *pszExpandedMetadataKey =  | 
1734  | 0  |           msOWSGetExpandedMetadataKey(namespaces, name);  | 
1735  | 0  |       msIO_fprintf(stream,  | 
1736  | 0  |                    "<!-- WARNING: Mandatory metadata %s was missing in this "  | 
1737  | 0  |                    "context. -->\n",  | 
1738  | 0  |                    pszExpandedMetadataKey);  | 
1739  | 0  |       msFree(pszExpandedMetadataKey);  | 
1740  | 0  |       status = action_if_not_found;  | 
1741  | 0  |     }  | 
1742  |  | 
  | 
1743  | 0  |     if (default_value)  | 
1744  | 0  |       msIO_fprintf(stream, format, default_value);  | 
1745  | 0  |   }  | 
1746  |  | 
  | 
1747  | 0  |   return status;  | 
1748  | 0  | }  | 
1749  |  |  | 
1750  |  | /*  | 
1751  |  | ** msOWSPrintEncodeMetadata()  | 
1752  |  | **  | 
1753  |  | ** Attempt to output a capability item.  If corresponding metadata is not  | 
1754  |  | ** found then one of a number of predefined actions will be taken.  | 
1755  |  | ** If a default value is provided and metadata is absent then the  | 
1756  |  | ** default will be used.  | 
1757  |  | ** Also encode the value with msEncodeHTMLEntities.  | 
1758  |  | */  | 
1759  |  |  | 
1760  |  | int msOWSPrintEncodeMetadata(FILE *stream, hashTableObj *metadata,  | 
1761  |  |                              const char *namespaces, const char *name,  | 
1762  |  |                              int action_if_not_found, const char *format,  | 
1763  | 0  |                              const char *default_value) { | 
1764  | 0  |   return msOWSPrintEncodeMetadata2(stream, metadata, namespaces, name,  | 
1765  | 0  |                                    action_if_not_found, format, default_value,  | 
1766  | 0  |                                    NULL);  | 
1767  | 0  | }  | 
1768  |  |  | 
1769  |  | /*  | 
1770  |  | ** msOWSPrintEncodeMetadata2()  | 
1771  |  | **  | 
1772  |  | ** Attempt to output a capability item in the requested language.  | 
1773  |  | ** Fallback using no language parameter.  | 
1774  |  | */  | 
1775  |  | int msOWSPrintEncodeMetadata2(FILE *stream, hashTableObj *metadata,  | 
1776  |  |                               const char *namespaces, const char *name,  | 
1777  |  |                               int action_if_not_found, const char *format,  | 
1778  |  |                               const char *default_value,  | 
1779  | 0  |                               const char *validated_language) { | 
1780  | 0  |   const char *value;  | 
1781  | 0  |   char *pszEncodedValue = NULL;  | 
1782  | 0  |   int status = MS_NOERR;  | 
1783  |  | 
  | 
1784  | 0  |   if ((value = msOWSLookupMetadataWithLanguage(metadata, namespaces, name,  | 
1785  | 0  |                                                validated_language))) { | 
1786  | 0  |     pszEncodedValue = msEncodeHTMLEntities(value);  | 
1787  | 0  |     msIO_fprintf(stream, format, pszEncodedValue);  | 
1788  | 0  |     free(pszEncodedValue);  | 
1789  | 0  |   } else { | 
1790  | 0  |     if (action_if_not_found == OWS_WARN) { | 
1791  | 0  |       char *pszExpandedName = msStringConcatenate(NULL, name);  | 
1792  | 0  |       if (validated_language && validated_language[0]) { | 
1793  | 0  |         pszExpandedName = msStringConcatenate(pszExpandedName, ".");  | 
1794  | 0  |         pszExpandedName =  | 
1795  | 0  |             msStringConcatenate(pszExpandedName, validated_language);  | 
1796  | 0  |       }  | 
1797  | 0  |       char *pszExpandedMetadataKey =  | 
1798  | 0  |           msOWSGetExpandedMetadataKey(namespaces, pszExpandedName);  | 
1799  | 0  |       msIO_fprintf(stream,  | 
1800  | 0  |                    "<!-- WARNING: Mandatory metadata %s was missing in this "  | 
1801  | 0  |                    "context. -->\n",  | 
1802  | 0  |                    pszExpandedMetadataKey);  | 
1803  | 0  |       msFree(pszExpandedName);  | 
1804  | 0  |       msFree(pszExpandedMetadataKey);  | 
1805  | 0  |       status = action_if_not_found;  | 
1806  | 0  |     }  | 
1807  |  | 
  | 
1808  | 0  |     if (default_value) { | 
1809  | 0  |       pszEncodedValue = msEncodeHTMLEntities(default_value);  | 
1810  | 0  |       msIO_fprintf(stream, format, default_value);  | 
1811  | 0  |       free(pszEncodedValue);  | 
1812  | 0  |     }  | 
1813  | 0  |   }  | 
1814  |  | 
  | 
1815  | 0  |   return status;  | 
1816  | 0  | }  | 
1817  |  |  | 
1818  |  | /*  | 
1819  |  | ** msOWSGetEncodeMetadata()  | 
1820  |  | **  | 
1821  |  | ** Equivalent to msOWSPrintEncodeMetadata. Returns en encoded value of the  | 
1822  |  | ** metadata or the default value.  | 
1823  |  | ** Caller should free the returned string.  | 
1824  |  | */  | 
1825  |  | char *msOWSGetEncodeMetadata(hashTableObj *metadata, const char *namespaces,  | 
1826  | 0  |                              const char *name, const char *default_value) { | 
1827  | 0  |   const char *value;  | 
1828  | 0  |   char *pszEncodedValue = NULL;  | 
1829  | 0  |   if ((value = msOWSLookupMetadata(metadata, namespaces, name)))  | 
1830  | 0  |     pszEncodedValue = msEncodeHTMLEntities(value);  | 
1831  | 0  |   else if (default_value)  | 
1832  | 0  |     pszEncodedValue = msEncodeHTMLEntities(default_value);  | 
1833  |  | 
  | 
1834  | 0  |   return pszEncodedValue;  | 
1835  | 0  | }  | 
1836  |  |  | 
1837  |  | /*  | 
1838  |  | ** msOWSPrintValidateMetadata()  | 
1839  |  | **  | 
1840  |  | ** Attempt to output a capability item.  If corresponding metadata is not  | 
1841  |  | ** found then one of a number of predefined actions will be taken.  | 
1842  |  | ** If a default value is provided and metadata is absent then the  | 
1843  |  | ** default will be used.  | 
1844  |  | ** Also validate the value with msIsXMLTagValid.  | 
1845  |  | */  | 
1846  |  |  | 
1847  |  | int msOWSPrintValidateMetadata(FILE *stream, hashTableObj *metadata,  | 
1848  |  |                                const char *namespaces, const char *name,  | 
1849  |  |                                int action_if_not_found, const char *format,  | 
1850  | 0  |                                const char *default_value) { | 
1851  | 0  |   const char *value;  | 
1852  | 0  |   int status = MS_NOERR;  | 
1853  |  | 
  | 
1854  | 0  |   if ((value = msOWSLookupMetadata(metadata, namespaces, name))) { | 
1855  | 0  |     if (msIsXMLTagValid(value) == MS_FALSE)  | 
1856  | 0  |       msIO_fprintf(stream,  | 
1857  | 0  |                    "<!-- WARNING: The value '%s' is not valid in a "  | 
1858  | 0  |                    "XML tag context. -->\n",  | 
1859  | 0  |                    value);  | 
1860  | 0  |     msIO_fprintf(stream, format, value);  | 
1861  | 0  |   } else { | 
1862  | 0  |     if (action_if_not_found == OWS_WARN) { | 
1863  | 0  |       char *pszExpandedMetadataKey =  | 
1864  | 0  |           msOWSGetExpandedMetadataKey(namespaces, name);  | 
1865  | 0  |       msIO_fprintf(stream,  | 
1866  | 0  |                    "<!-- WARNING: Mandatory metadata %s was missing in this "  | 
1867  | 0  |                    "context. -->\n",  | 
1868  | 0  |                    pszExpandedMetadataKey);  | 
1869  | 0  |       msFree(pszExpandedMetadataKey);  | 
1870  | 0  |       status = action_if_not_found;  | 
1871  | 0  |     }  | 
1872  |  | 
  | 
1873  | 0  |     if (default_value) { | 
1874  | 0  |       if (msIsXMLTagValid(default_value) == MS_FALSE)  | 
1875  | 0  |         msIO_fprintf(stream,  | 
1876  | 0  |                      "<!-- WARNING: The value '%s' is not valid "  | 
1877  | 0  |                      "in a XML tag context. -->\n",  | 
1878  | 0  |                      default_value);  | 
1879  | 0  |       msIO_fprintf(stream, format, default_value);  | 
1880  | 0  |     }  | 
1881  | 0  |   }  | 
1882  |  | 
  | 
1883  | 0  |   return status;  | 
1884  | 0  | }  | 
1885  |  |  | 
1886  |  | /*  | 
1887  |  | ** msOWSPrintGroupMetadata()  | 
1888  |  | **  | 
1889  |  | ** Attempt to output a capability item.  If corresponding metadata is not  | 
1890  |  | ** found then one of a number of predefined actions will be taken.  | 
1891  |  | ** If a default value is provided and metadata is absent then the  | 
1892  |  | ** default will be used.  | 
1893  |  | */  | 
1894  |  | int msOWSPrintGroupMetadata(FILE *stream, mapObj *map, char *pszGroupName,  | 
1895  |  |                             const char *namespaces, const char *name,  | 
1896  |  |                             int action_if_not_found, const char *format,  | 
1897  | 0  |                             const char *default_value) { | 
1898  | 0  |   return msOWSPrintGroupMetadata2(stream, map, pszGroupName, namespaces, name,  | 
1899  | 0  |                                   action_if_not_found, format, default_value,  | 
1900  | 0  |                                   NULL);  | 
1901  | 0  | }  | 
1902  |  |  | 
1903  |  | /*  | 
1904  |  | ** msOWSPrintGroupMetadata2()  | 
1905  |  | **  | 
1906  |  | ** Attempt to output a capability item in the requested language.  | 
1907  |  | ** Fallback using no language parameter.  | 
1908  |  | */  | 
1909  |  | int msOWSPrintGroupMetadata2(FILE *stream, mapObj *map, char *pszGroupName,  | 
1910  |  |                              const char *namespaces, const char *name,  | 
1911  |  |                              int action_if_not_found, const char *format,  | 
1912  |  |                              const char *default_value,  | 
1913  | 0  |                              const char *validated_language) { | 
1914  | 0  |   const char *value;  | 
1915  | 0  |   char *encoded;  | 
1916  | 0  |   int status = MS_NOERR;  | 
1917  | 0  |   int i;  | 
1918  |  | 
  | 
1919  | 0  |   for (i = 0; i < map->numlayers; i++) { | 
1920  | 0  |     if (GET_LAYER(map, i)->group &&  | 
1921  | 0  |         (strcmp(GET_LAYER(map, i)->group, pszGroupName) == 0)) { | 
1922  | 0  |       if ((value = msOWSLookupMetadataWithLanguage(  | 
1923  | 0  |                &(GET_LAYER(map, i)->metadata), namespaces, name,  | 
1924  | 0  |                validated_language))) { | 
1925  | 0  |         encoded = msEncodeHTMLEntities(value);  | 
1926  | 0  |         msIO_fprintf(stream, format, encoded);  | 
1927  | 0  |         msFree(encoded);  | 
1928  | 0  |         return status;  | 
1929  | 0  |       }  | 
1930  | 0  |     }  | 
1931  | 0  |   }  | 
1932  |  |  | 
1933  | 0  |   if (action_if_not_found == OWS_WARN) { | 
1934  | 0  |     char *pszExpandedMetadataKey =  | 
1935  | 0  |         msOWSGetExpandedMetadataKey(namespaces, name);  | 
1936  | 0  |     msIO_fprintf(stream,  | 
1937  | 0  |                  "<!-- WARNING: Mandatory metadata %s was missing in this "  | 
1938  | 0  |                  "context. -->\n",  | 
1939  | 0  |                  pszExpandedMetadataKey);  | 
1940  | 0  |     msFree(pszExpandedMetadataKey);  | 
1941  | 0  |     status = action_if_not_found;  | 
1942  | 0  |   }  | 
1943  |  | 
  | 
1944  | 0  |   if (default_value) { | 
1945  | 0  |     encoded = msEncodeHTMLEntities(default_value);  | 
1946  | 0  |     msIO_fprintf(stream, format, encoded);  | 
1947  | 0  |     msFree(encoded);  | 
1948  | 0  |   }  | 
1949  |  | 
  | 
1950  | 0  |   return status;  | 
1951  | 0  | }  | 
1952  |  |  | 
1953  |  | /* msOWSPrintURLType()  | 
1954  |  | **  | 
1955  |  | ** Attempt to output a URL item in capabilities.  If corresponding metadata  | 
1956  |  | ** is not found then one of a number of predefined actions will be taken.  | 
1957  |  | ** Since it's a capability item, five metadata will be used to populate the  | 
1958  |  | ** XML elements.  | 
1959  |  | **  | 
1960  |  | ** The 'name' argument is the basename of the metadata items relating to this  | 
1961  |  | ** URL type and the suffixes _type, _width, _height, _format and _href will  | 
1962  |  | ** be appended to the name in the metadata search.  | 
1963  |  | ** e.g. passing name=metadataurl will result in the following medata entries  | 
1964  |  | ** being used:  | 
1965  |  | **    ows_metadataurl_type  | 
1966  |  | **    ows_metadataurl_format  | 
1967  |  | **    ows_metadataurl_href  | 
1968  |  | **    ... (width and height are unused for metadata)  | 
1969  |  | **  | 
1970  |  | ** As for all the msOWSPrint*() functions, the namespace argument specifies  | 
1971  |  | ** which prefix (ows_, wms_, wcs_, etc.) is used for the metadata names above.  | 
1972  |  | **  | 
1973  |  | ** Then the final string will be built from  | 
1974  |  | ** the tag_name and the five metadata. The template is:  | 
1975  |  | ** <tag_name%type%width%height%format>%href</tag_name>  | 
1976  |  | **  | 
1977  |  | ** For example the width format will usually be " width=\"%s\"".  | 
1978  |  | ** An extern format will be "> <Format>%s</Format"  | 
1979  |  | **  | 
1980  |  | ** Another template template may be used, but it needs to contains 5 %s,  | 
1981  |  | ** otherwise leave it to NULL. If tag_format is used then you don't need the  | 
1982  |  | ** tag_name and the tabspace.  | 
1983  |  | **  | 
1984  |  | ** Note that all values will be HTML-encoded.  | 
1985  |  | **/  | 
1986  |  | int msOWSPrintURLType(FILE *stream, hashTableObj *metadata,  | 
1987  |  |                       const char *namespaces, const char *name,  | 
1988  |  |                       int action_if_not_found, const char *tag_format,  | 
1989  |  |                       const char *tag_name, const char *type_format,  | 
1990  |  |                       const char *width_format, const char *height_format,  | 
1991  |  |                       const char *urlfrmt_format, const char *href_format,  | 
1992  |  |                       int type_is_mandatory, int width_is_mandatory,  | 
1993  |  |                       int height_is_mandatory, int format_is_mandatory,  | 
1994  |  |                       int href_is_mandatory, const char *default_type,  | 
1995  |  |                       const char *default_width, const char *default_height,  | 
1996  |  |                       const char *default_urlfrmt, const char *default_href,  | 
1997  | 0  |                       const char *tabspace) { | 
1998  | 0  |   const char *value;  | 
1999  | 0  |   char *metadata_name;  | 
2000  | 0  |   char *encoded;  | 
2001  | 0  |   int status = MS_NOERR;  | 
2002  | 0  |   char *type = NULL, *width = NULL, *height = NULL, *urlfrmt = NULL,  | 
2003  | 0  |        *href = NULL;  | 
2004  |  | 
  | 
2005  | 0  |   const size_t buffer_size = strlen(name) + 10;  | 
2006  | 0  |   metadata_name = (char *)malloc(buffer_size);  | 
2007  |  |  | 
2008  |  |   /* Get type */  | 
2009  | 0  |   if (type_format != NULL) { | 
2010  | 0  |     snprintf(metadata_name, buffer_size, "%s_type", name);  | 
2011  | 0  |     value = msOWSLookupMetadata(metadata, namespaces, metadata_name);  | 
2012  | 0  |     if (value != NULL) { | 
2013  | 0  |       encoded = msEncodeHTMLEntities(value);  | 
2014  | 0  |       const size_t buffer_size_tmp = strlen(type_format) + strlen(encoded) + 1;  | 
2015  | 0  |       type = (char *)malloc(buffer_size_tmp);  | 
2016  | 0  |       snprintf(type, buffer_size_tmp, type_format, encoded);  | 
2017  | 0  |       msFree(encoded);  | 
2018  | 0  |     }  | 
2019  | 0  |   }  | 
2020  |  |  | 
2021  |  |   /* Get width */  | 
2022  | 0  |   if (width_format != NULL) { | 
2023  | 0  |     snprintf(metadata_name, buffer_size, "%s_width", name);  | 
2024  | 0  |     value = msOWSLookupMetadata(metadata, namespaces, metadata_name);  | 
2025  | 0  |     if (value != NULL) { | 
2026  | 0  |       encoded = msEncodeHTMLEntities(value);  | 
2027  | 0  |       const size_t buffer_size_tmp = strlen(width_format) + strlen(encoded) + 1;  | 
2028  | 0  |       width = (char *)malloc(buffer_size_tmp);  | 
2029  | 0  |       snprintf(width, buffer_size_tmp, width_format, encoded);  | 
2030  | 0  |       msFree(encoded);  | 
2031  | 0  |     }  | 
2032  | 0  |   }  | 
2033  |  |  | 
2034  |  |   /* Get height */  | 
2035  | 0  |   if (height_format != NULL) { | 
2036  | 0  |     snprintf(metadata_name, buffer_size, "%s_height", name);  | 
2037  | 0  |     value = msOWSLookupMetadata(metadata, namespaces, metadata_name);  | 
2038  | 0  |     if (value != NULL) { | 
2039  | 0  |       encoded = msEncodeHTMLEntities(value);  | 
2040  | 0  |       const size_t buffer_size_tmp =  | 
2041  | 0  |           strlen(height_format) + strlen(encoded) + 1;  | 
2042  | 0  |       height = (char *)malloc(buffer_size_tmp);  | 
2043  | 0  |       snprintf(height, buffer_size_tmp, height_format, encoded);  | 
2044  | 0  |       msFree(encoded);  | 
2045  | 0  |     }  | 
2046  | 0  |   }  | 
2047  |  |  | 
2048  |  |   /* Get format */  | 
2049  | 0  |   if (urlfrmt_format != NULL) { | 
2050  | 0  |     snprintf(metadata_name, buffer_size, "%s_format", name);  | 
2051  | 0  |     value = msOWSLookupMetadata(metadata, namespaces, metadata_name);  | 
2052  | 0  |     if (value != NULL) { | 
2053  | 0  |       encoded = msEncodeHTMLEntities(value);  | 
2054  | 0  |       const size_t buffer_size_tmp =  | 
2055  | 0  |           strlen(urlfrmt_format) + strlen(encoded) + 1;  | 
2056  | 0  |       urlfrmt = (char *)malloc(buffer_size_tmp);  | 
2057  | 0  |       snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, encoded);  | 
2058  | 0  |       msFree(encoded);  | 
2059  | 0  |     }  | 
2060  | 0  |   }  | 
2061  |  |  | 
2062  |  |   /* Get href */  | 
2063  | 0  |   if (href_format != NULL) { | 
2064  | 0  |     snprintf(metadata_name, buffer_size, "%s_href", name);  | 
2065  | 0  |     value = msOWSLookupMetadata(metadata, namespaces, metadata_name);  | 
2066  | 0  |     if (value != NULL) { | 
2067  | 0  |       encoded = msEncodeHTMLEntities(value);  | 
2068  | 0  |       const size_t buffer_size_tmp = strlen(href_format) + strlen(encoded) + 1;  | 
2069  | 0  |       href = (char *)malloc(buffer_size_tmp);  | 
2070  | 0  |       snprintf(href, buffer_size_tmp, href_format, encoded);  | 
2071  | 0  |       msFree(encoded);  | 
2072  | 0  |     }  | 
2073  | 0  |   }  | 
2074  |  | 
  | 
2075  | 0  |   msFree(metadata_name);  | 
2076  |  | 
  | 
2077  | 0  |   if (type || width || height || urlfrmt || href ||  | 
2078  | 0  |       (!metadata && (default_type || default_width || default_height ||  | 
2079  | 0  |                      default_urlfrmt || default_href))) { | 
2080  | 0  |     if ((!type && type_is_mandatory) || (!width && width_is_mandatory) ||  | 
2081  | 0  |         (!height && height_is_mandatory) || (!urlfrmt && format_is_mandatory) ||  | 
2082  | 0  |         (!href && href_is_mandatory)) { | 
2083  | 0  |       msIO_fprintf(stream,  | 
2084  | 0  |                    "<!-- WARNING: Some mandatory elements for '%s' are missing "  | 
2085  | 0  |                    "in this context. -->\n",  | 
2086  | 0  |                    tag_name);  | 
2087  | 0  |       if (action_if_not_found == OWS_WARN) { | 
2088  | 0  |         char *pszExpandedMetadataKey =  | 
2089  | 0  |             msOWSGetExpandedMetadataKey(namespaces, name);  | 
2090  | 0  |         msIO_fprintf(stream,  | 
2091  | 0  |                      "<!-- WARNING: Mandatory metadata %s was missing in this "  | 
2092  | 0  |                      "context. -->\n",  | 
2093  | 0  |                      pszExpandedMetadataKey);  | 
2094  | 0  |         msFree(pszExpandedMetadataKey);  | 
2095  | 0  |         status = action_if_not_found;  | 
2096  | 0  |       }  | 
2097  | 0  |     } else { | 
2098  | 0  |       if (!type && type_format && default_type) { | 
2099  | 0  |         const size_t buffer_size_tmp =  | 
2100  | 0  |             strlen(type_format) + strlen(default_type) + 2;  | 
2101  | 0  |         type = (char *)malloc(buffer_size_tmp);  | 
2102  | 0  |         snprintf(type, buffer_size_tmp, type_format, default_type);  | 
2103  | 0  |       } else if (!type)  | 
2104  | 0  |         type = msStrdup(""); | 
2105  | 0  |       if (!width && width_format && default_width) { | 
2106  | 0  |         const size_t buffer_size_tmp =  | 
2107  | 0  |             strlen(width_format) + strlen(default_width) + 2;  | 
2108  | 0  |         width = (char *)malloc(buffer_size_tmp);  | 
2109  | 0  |         snprintf(width, buffer_size_tmp, width_format, default_width);  | 
2110  | 0  |       } else if (!width)  | 
2111  | 0  |         width = msStrdup(""); | 
2112  | 0  |       if (!height && height_format && default_height) { | 
2113  | 0  |         const size_t buffer_size_tmp =  | 
2114  | 0  |             strlen(height_format) + strlen(default_height) + 2;  | 
2115  | 0  |         height = (char *)malloc(buffer_size_tmp);  | 
2116  | 0  |         snprintf(height, buffer_size_tmp, height_format, default_height);  | 
2117  | 0  |       } else if (!height)  | 
2118  | 0  |         height = msStrdup(""); | 
2119  | 0  |       if (!urlfrmt && urlfrmt_format && default_urlfrmt) { | 
2120  | 0  |         const size_t buffer_size_tmp =  | 
2121  | 0  |             strlen(urlfrmt_format) + strlen(default_urlfrmt) + 2;  | 
2122  | 0  |         urlfrmt = (char *)malloc(buffer_size_tmp);  | 
2123  | 0  |         snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, default_urlfrmt);  | 
2124  | 0  |       } else if (!urlfrmt)  | 
2125  | 0  |         urlfrmt = msStrdup(""); | 
2126  | 0  |       if (!href && href_format && default_href) { | 
2127  | 0  |         const size_t buffer_size_tmp =  | 
2128  | 0  |             strlen(href_format) + strlen(default_href) + 2;  | 
2129  | 0  |         href = (char *)malloc(buffer_size_tmp);  | 
2130  | 0  |         snprintf(href, buffer_size_tmp, href_format, default_href);  | 
2131  | 0  |       } else if (!href)  | 
2132  | 0  |         href = msStrdup(""); | 
2133  |  | 
  | 
2134  | 0  |       if (tag_format == NULL)  | 
2135  | 0  |         msIO_fprintf(stream, "%s<%s%s%s%s%s>%s</%s>\n", tabspace, tag_name,  | 
2136  | 0  |                      type, width, height, urlfrmt, href, tag_name);  | 
2137  | 0  |       else  | 
2138  | 0  |         msIO_fprintf(stream, tag_format, type, width, height, urlfrmt, href);  | 
2139  | 0  |     }  | 
2140  |  | 
  | 
2141  | 0  |     msFree(type);  | 
2142  | 0  |     msFree(width);  | 
2143  | 0  |     msFree(height);  | 
2144  | 0  |     msFree(urlfrmt);  | 
2145  | 0  |     msFree(href);  | 
2146  | 0  |   } else { | 
2147  | 0  |     if (action_if_not_found == OWS_WARN) { | 
2148  | 0  |       char *pszExpandedMetadataKey =  | 
2149  | 0  |           msOWSGetExpandedMetadataKey(namespaces, name);  | 
2150  | 0  |       msIO_fprintf(stream,  | 
2151  | 0  |                    "<!-- WARNING: Mandatory metadata %s was missing in this "  | 
2152  | 0  |                    "context. -->\n",  | 
2153  | 0  |                    pszExpandedMetadataKey);  | 
2154  | 0  |       msFree(pszExpandedMetadataKey);  | 
2155  | 0  |       status = action_if_not_found;  | 
2156  | 0  |     }  | 
2157  | 0  |   }  | 
2158  |  | 
  | 
2159  | 0  |   return status;  | 
2160  | 0  | }  | 
2161  |  |  | 
2162  |  | /* msOWSPrintParam()  | 
2163  |  | **  | 
2164  |  | ** Same as printMetadata() but applied to mapfile parameters.  | 
2165  |  | **/  | 
2166  |  | int msOWSPrintParam(FILE *stream, const char *name, const char *value,  | 
2167  |  |                     int action_if_not_found, const char *format,  | 
2168  | 0  |                     const char *default_value) { | 
2169  | 0  |   int status = MS_NOERR;  | 
2170  |  | 
  | 
2171  | 0  |   if (value && strlen(value) > 0) { | 
2172  | 0  |     msIO_fprintf(stream, format, value);  | 
2173  | 0  |   } else { | 
2174  | 0  |     if (action_if_not_found == OWS_WARN) { | 
2175  | 0  |       msIO_fprintf(stream,  | 
2176  | 0  |                    "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "  | 
2177  | 0  |                    "in this context. -->\n",  | 
2178  | 0  |                    name);  | 
2179  | 0  |       status = action_if_not_found;  | 
2180  | 0  |     }  | 
2181  |  | 
  | 
2182  | 0  |     if (default_value)  | 
2183  | 0  |       msIO_fprintf(stream, format, default_value);  | 
2184  | 0  |   }  | 
2185  |  | 
  | 
2186  | 0  |   return status;  | 
2187  | 0  | }  | 
2188  |  |  | 
2189  |  | /* msOWSPrintEncodeParam()  | 
2190  |  | **  | 
2191  |  | ** Same as printEncodeMetadata() but applied to mapfile parameters.  | 
2192  |  | **/  | 
2193  |  | int msOWSPrintEncodeParam(FILE *stream, const char *name, const char *value,  | 
2194  |  |                           int action_if_not_found, const char *format,  | 
2195  | 0  |                           const char *default_value) { | 
2196  | 0  |   int status = MS_NOERR;  | 
2197  | 0  |   char *encode;  | 
2198  |  | 
  | 
2199  | 0  |   if (value && strlen(value) > 0) { | 
2200  | 0  |     encode = msEncodeHTMLEntities(value);  | 
2201  | 0  |     msIO_fprintf(stream, format, encode);  | 
2202  | 0  |     msFree(encode);  | 
2203  | 0  |   } else { | 
2204  | 0  |     if (action_if_not_found == OWS_WARN) { | 
2205  | 0  |       msIO_fprintf(stream,  | 
2206  | 0  |                    "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "  | 
2207  | 0  |                    "in this context. -->\n",  | 
2208  | 0  |                    name);  | 
2209  | 0  |       status = action_if_not_found;  | 
2210  | 0  |     }  | 
2211  |  | 
  | 
2212  | 0  |     if (default_value) { | 
2213  | 0  |       encode = msEncodeHTMLEntities(default_value);  | 
2214  | 0  |       msIO_fprintf(stream, format, encode);  | 
2215  | 0  |       msFree(encode);  | 
2216  | 0  |     }  | 
2217  | 0  |   }  | 
2218  |  | 
  | 
2219  | 0  |   return status;  | 
2220  | 0  | }  | 
2221  |  |  | 
2222  |  | /* msOWSPrintMetadataList()  | 
2223  |  | **  | 
2224  |  | ** Prints comma-separated lists metadata.  (e.g. keywordList)  | 
2225  |  | ** default_value serves 2 purposes if specified:  | 
2226  |  | ** - won't be printed if part of MetadataList (default_value == key"_exclude")  | 
2227  |  | **  (exclusion)  | 
2228  |  | ** - will be printed if MetadataList is empty (fallback)  | 
2229  |  | **/  | 
2230  |  | int msOWSPrintMetadataList(FILE *stream, hashTableObj *metadata,  | 
2231  |  |                            const char *namespaces, const char *name,  | 
2232  |  |                            const char *startTag, const char *endTag,  | 
2233  | 0  |                            const char *itemFormat, const char *default_value) { | 
2234  | 0  |   const char *value;  | 
2235  |  | 
  | 
2236  | 0  |   value = msOWSLookupMetadata(metadata, namespaces, name);  | 
2237  |  | 
  | 
2238  | 0  |   if (value == NULL) { | 
2239  | 0  |     value = default_value;  | 
2240  | 0  |     default_value = NULL;  | 
2241  | 0  |   }  | 
2242  |  | 
  | 
2243  | 0  |   if (value != NULL) { | 
2244  | 0  |     char **keywords;  | 
2245  | 0  |     int numkeywords;  | 
2246  |  | 
  | 
2247  | 0  |     keywords = msStringSplit(value, ',', &numkeywords);  | 
2248  | 0  |     if (keywords && numkeywords > 0) { | 
2249  | 0  |       int kw;  | 
2250  | 0  |       if (startTag)  | 
2251  | 0  |         msIO_fprintf(stream, "%s", startTag);  | 
2252  | 0  |       for (kw = 0; kw < numkeywords; kw++) { | 
2253  | 0  |         if (default_value != NULL &&  | 
2254  | 0  |             strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==  | 
2255  | 0  |                 0 &&  | 
2256  | 0  |             strncasecmp("_exclude", default_value + strlen(default_value) - 8, | 
2257  | 0  |                         8) == 0)  | 
2258  | 0  |           continue;  | 
2259  |  |  | 
2260  | 0  |         msIO_fprintf(stream, itemFormat, keywords[kw]);  | 
2261  | 0  |       }  | 
2262  | 0  |       if (endTag)  | 
2263  | 0  |         msIO_fprintf(stream, "%s", endTag);  | 
2264  | 0  |     }  | 
2265  | 0  |     msFreeCharArray(keywords, numkeywords);  | 
2266  | 0  |     return MS_TRUE;  | 
2267  | 0  |   }  | 
2268  | 0  |   return MS_FALSE;  | 
2269  | 0  | }  | 
2270  |  |  | 
2271  |  | /* msOWSPrintEncodeMetadataList()  | 
2272  |  | **  | 
2273  |  | ** Prints comma-separated lists metadata.  (e.g. keywordList)  | 
2274  |  | ** This will print HTML encoded values.  | 
2275  |  | ** default_value serves 2 purposes if specified:  | 
2276  |  | ** - won't be printed if part of MetadataList (default_value == key"_exclude")  | 
2277  |  | **  (exclusion)  | 
2278  |  | ** - will be printed if MetadataList is empty (fallback)  | 
2279  |  | **/  | 
2280  |  | int msOWSPrintEncodeMetadataList(FILE *stream, hashTableObj *metadata,  | 
2281  |  |                                  const char *namespaces, const char *name,  | 
2282  |  |                                  const char *startTag, const char *endTag,  | 
2283  |  |                                  const char *itemFormat,  | 
2284  | 0  |                                  const char *default_value) { | 
2285  | 0  |   const char *value;  | 
2286  | 0  |   char *encoded;  | 
2287  | 0  |   size_t default_value_len = 0;  | 
2288  |  | 
  | 
2289  | 0  |   value = msOWSLookupMetadata(metadata, namespaces, name);  | 
2290  |  | 
  | 
2291  | 0  |   if (value == NULL) { | 
2292  | 0  |     value = default_value;  | 
2293  | 0  |     default_value = NULL;  | 
2294  | 0  |   }  | 
2295  | 0  |   if (default_value)  | 
2296  | 0  |     default_value_len = strlen(default_value);  | 
2297  |  | 
  | 
2298  | 0  |   if (value != NULL) { | 
2299  | 0  |     char **keywords;  | 
2300  | 0  |     int numkeywords;  | 
2301  |  | 
  | 
2302  | 0  |     keywords = msStringSplit(value, ',', &numkeywords);  | 
2303  | 0  |     if (keywords && numkeywords > 0) { | 
2304  | 0  |       int kw;  | 
2305  | 0  |       if (startTag)  | 
2306  | 0  |         msIO_fprintf(stream, "%s", startTag);  | 
2307  | 0  |       for (kw = 0; kw < numkeywords; kw++) { | 
2308  | 0  |         if (default_value != NULL && default_value_len > 8 &&  | 
2309  | 0  |             strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==  | 
2310  | 0  |                 0 &&  | 
2311  | 0  |             strncasecmp("_exclude", default_value + default_value_len - 8, 8) == | 
2312  | 0  |                 0)  | 
2313  | 0  |           continue;  | 
2314  |  |  | 
2315  | 0  |         encoded = msEncodeHTMLEntities(keywords[kw]);  | 
2316  | 0  |         msIO_fprintf(stream, itemFormat, encoded);  | 
2317  | 0  |         msFree(encoded);  | 
2318  | 0  |       }  | 
2319  | 0  |       if (endTag)  | 
2320  | 0  |         msIO_fprintf(stream, "%s", endTag);  | 
2321  | 0  |     }  | 
2322  | 0  |     msFreeCharArray(keywords, numkeywords);  | 
2323  | 0  |     return MS_TRUE;  | 
2324  | 0  |   }  | 
2325  | 0  |   return MS_FALSE;  | 
2326  | 0  | }  | 
2327  |  |  | 
2328  |  | /* msOWSPrintEncodeParamList()  | 
2329  |  | **  | 
2330  |  | ** Same as msOWSPrintEncodeMetadataList() but applied to mapfile parameters.  | 
2331  |  | **/  | 
2332  |  | int msOWSPrintEncodeParamList(FILE *stream, const char *name, const char *value,  | 
2333  |  |                               int action_if_not_found, char delimiter,  | 
2334  |  |                               const char *startTag, const char *endTag,  | 
2335  | 0  |                               const char *format, const char *default_value) { | 
2336  | 0  |   int status = MS_NOERR;  | 
2337  | 0  |   char *encoded;  | 
2338  | 0  |   char **items = NULL;  | 
2339  | 0  |   int numitems = 0, i;  | 
2340  |  | 
  | 
2341  | 0  |   if (value && strlen(value) > 0)  | 
2342  | 0  |     items = msStringSplit(value, delimiter, &numitems);  | 
2343  | 0  |   else { | 
2344  | 0  |     if (action_if_not_found == OWS_WARN) { | 
2345  | 0  |       msIO_fprintf(stream,  | 
2346  | 0  |                    "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "  | 
2347  | 0  |                    "in this context. -->\n",  | 
2348  | 0  |                    name);  | 
2349  | 0  |       status = action_if_not_found;  | 
2350  | 0  |     }  | 
2351  |  | 
  | 
2352  | 0  |     if (default_value)  | 
2353  | 0  |       items = msStringSplit(default_value, delimiter, &numitems);  | 
2354  | 0  |   }  | 
2355  |  | 
  | 
2356  | 0  |   if (items && numitems > 0) { | 
2357  | 0  |     if (startTag)  | 
2358  | 0  |       msIO_fprintf(stream, "%s", startTag);  | 
2359  | 0  |     for (i = 0; i < numitems; i++) { | 
2360  | 0  |       encoded = msEncodeHTMLEntities(items[i]);  | 
2361  | 0  |       msIO_fprintf(stream, format, encoded);  | 
2362  | 0  |       msFree(encoded);  | 
2363  | 0  |     }  | 
2364  | 0  |     if (endTag)  | 
2365  | 0  |       msIO_fprintf(stream, "%s", endTag);  | 
2366  | 0  |   }  | 
2367  | 0  |   msFreeCharArray(items, numitems);  | 
2368  |  | 
  | 
2369  | 0  |   return status;  | 
2370  | 0  | }  | 
2371  |  |  | 
2372  |  | /*  | 
2373  |  | ** msOWSPrintEX_GeographicBoundingBox()  | 
2374  |  | **  | 
2375  |  | ** Print a EX_GeographicBoundingBox tag for WMS1.3.0  | 
2376  |  | **  | 
2377  |  | */  | 
2378  |  | void msOWSPrintEX_GeographicBoundingBox(FILE *stream, const char *tabspace,  | 
2379  |  |                                         rectObj *extent, projectionObj *srcproj)  | 
2380  |  |  | 
2381  | 0  | { | 
2382  | 0  |   const char *pszTag = "EX_GeographicBoundingBox"; /* The default for WMS */  | 
2383  | 0  |   rectObj ext;  | 
2384  |  | 
  | 
2385  | 0  |   ext = *extent;  | 
2386  |  |  | 
2387  |  |   /* always project to lat long */  | 
2388  | 0  |   msOWSProjectToWGS84(srcproj, &ext);  | 
2389  |  | 
  | 
2390  | 0  |   msIO_fprintf(stream, "%s<%s>\n", tabspace, pszTag);  | 
2391  | 0  |   msIO_fprintf(stream, "%s    <westBoundLongitude>%.6f</westBoundLongitude>\n",  | 
2392  | 0  |                tabspace, ext.minx);  | 
2393  | 0  |   msIO_fprintf(stream, "%s    <eastBoundLongitude>%.6f</eastBoundLongitude>\n",  | 
2394  | 0  |                tabspace, ext.maxx);  | 
2395  | 0  |   msIO_fprintf(stream, "%s    <southBoundLatitude>%.6f</southBoundLatitude>\n",  | 
2396  | 0  |                tabspace, ext.miny);  | 
2397  | 0  |   msIO_fprintf(stream, "%s    <northBoundLatitude>%.6f</northBoundLatitude>\n",  | 
2398  | 0  |                tabspace, ext.maxy);  | 
2399  | 0  |   msIO_fprintf(stream, "%s</%s>\n", tabspace, pszTag);  | 
2400  |  |  | 
2401  |  |   /* msIO_fprintf(stream, "%s<%s minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"  | 
2402  |  |      />\n", tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy); */  | 
2403  | 0  | }  | 
2404  |  |  | 
2405  |  | /*  | 
2406  |  | ** msOWSPrintLatLonBoundingBox()  | 
2407  |  | **  | 
2408  |  | ** Print a LatLonBoundingBox tag for WMS, or LatLongBoundingBox for WFS  | 
2409  |  | ** ... yes, the tag name differs between WMS and WFS, yuck!  | 
2410  |  | **  | 
2411  |  | */  | 
2412  |  | void msOWSPrintLatLonBoundingBox(FILE *stream, const char *tabspace,  | 
2413  |  |                                  rectObj *extent, projectionObj *srcproj,  | 
2414  |  |                                  projectionObj *wfsproj,  | 
2415  | 0  |                                  OWSServiceType nService) { | 
2416  | 0  |   const char *pszTag = "LatLonBoundingBox"; /* The default for WMS */  | 
2417  | 0  |   rectObj ext;  | 
2418  |  | 
  | 
2419  | 0  |   ext = *extent;  | 
2420  |  | 
  | 
2421  | 0  |   if (nService == OWS_WMS) { /* always project to lat long */ | 
2422  | 0  |     msOWSProjectToWGS84(srcproj, &ext);  | 
2423  | 0  |   } else if (nService == OWS_WFS) { /* called from wfs 1.0.0 only: project to | 
2424  |  |                                        map srs, if set */  | 
2425  | 0  |     pszTag = "LatLongBoundingBox";  | 
2426  | 0  |     if (wfsproj) { | 
2427  | 0  |       if (msProjectionsDiffer(srcproj, wfsproj) == MS_TRUE)  | 
2428  | 0  |         msProjectRect(srcproj, wfsproj, &ext);  | 
2429  | 0  |     }  | 
2430  | 0  |   }  | 
2431  |  | 
  | 
2432  | 0  |   msIO_fprintf(  | 
2433  | 0  |       stream,  | 
2434  | 0  |       "%s<%s minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" maxy=\"%.6f\" />\n",  | 
2435  | 0  |       tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy);  | 
2436  | 0  | }  | 
2437  |  |  | 
2438  |  | /*  | 
2439  |  | ** Emit a bounding box if we can find projection information.  | 
2440  |  | ** If <namespaces>_bbox_extended is not set, emit a single bounding box  | 
2441  |  | ** using the layer's native SRS (ignoring any <namespaces>_srs metadata).  | 
2442  |  | **  | 
2443  |  | ** If <namespaces>_bbox_extended is set to true, emit a bounding box  | 
2444  |  | ** for every projection listed in the <namespaces>_srs list.  | 
2445  |  | ** Check the map level metadata for both _bbox_extended and _srs,  | 
2446  |  | ** if there is no such metadata at the layer level.  | 
2447  |  | ** (These settings make more sense at the global/map level anyways)  | 
2448  |  | */  | 
2449  |  | void msOWSPrintBoundingBox(FILE *stream, const char *tabspace, rectObj *extent,  | 
2450  |  |                            projectionObj *srcproj, hashTableObj *layer_meta,  | 
2451  |  |                            hashTableObj *map_meta, const char *namespaces,  | 
2452  | 0  |                            int wms_version) { | 
2453  | 0  |   const char *value, *resx, *resy, *wms_bbox_extended;  | 
2454  | 0  |   char *encoded, *encoded_resx, *encoded_resy, *epsg_str;  | 
2455  | 0  |   char **epsgs;  | 
2456  | 0  |   int i, num_epsgs;  | 
2457  | 0  |   projectionObj proj;  | 
2458  | 0  |   rectObj ext;  | 
2459  |  | 
  | 
2460  | 0  |   wms_bbox_extended =  | 
2461  | 0  |       msOWSLookupMetadata2(layer_meta, map_meta, namespaces, "bbox_extended");  | 
2462  | 0  |   if (wms_bbox_extended && strncasecmp(wms_bbox_extended, "true", 5) == 0) { | 
2463  |  |     /* get a list of all projections from the metadata  | 
2464  |  |        try the layer metadata first, otherwise use the map's */  | 
2465  | 0  |     if (msOWSLookupMetadata(layer_meta, namespaces, "srs")) { | 
2466  | 0  |       msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_FALSE, &epsg_str);  | 
2467  | 0  |     } else { | 
2468  | 0  |       msOWSGetEPSGProj(srcproj, map_meta, namespaces, MS_FALSE, &epsg_str);  | 
2469  | 0  |     }  | 
2470  | 0  |     epsgs = msStringSplit(epsg_str, ' ', &num_epsgs);  | 
2471  | 0  |     msFree(epsg_str);  | 
2472  | 0  |   } else { | 
2473  |  |     /* Look for EPSG code in PROJECTION block only.  "wms_srs" metadata cannot  | 
2474  |  |      * be used to establish the native projection of a layer for BoundingBox  | 
2475  |  |      * purposes.  | 
2476  |  |      */  | 
2477  | 0  |     epsgs = (char **)msSmallMalloc(sizeof(char *));  | 
2478  | 0  |     num_epsgs = 1;  | 
2479  | 0  |     msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_TRUE, &(epsgs[0]));  | 
2480  | 0  |   }  | 
2481  |  | 
  | 
2482  | 0  |   for (i = 0; i < num_epsgs; i++) { | 
2483  | 0  |     value = epsgs[i];  | 
2484  | 0  |     if (value && *value) { | 
2485  | 0  |       memcpy(&ext, extent, sizeof(rectObj));  | 
2486  |  |  | 
2487  |  |       /* reproject the extents for each SRS's bounding box */  | 
2488  | 0  |       msInitProjection(&proj);  | 
2489  | 0  |       msProjectionInheritContextFrom(&proj, srcproj);  | 
2490  | 0  |       if (msLoadProjectionStringEPSG(&proj, (char *)value) == 0) { | 
2491  | 0  |         if (msProjectionsDiffer(srcproj, &proj) == MS_TRUE) { | 
2492  | 0  |           msProjectRect(srcproj, &proj, &ext);  | 
2493  | 0  |         }  | 
2494  |  |         /*for wms 1.3.0 we need to make sure that we present the BBOX with  | 
2495  |  |           a reversed axes for some espg codes*/  | 
2496  | 0  |         if (wms_version >= OWS_1_3_0 && strncasecmp(value, "EPSG:", 5) == 0) { | 
2497  | 0  |           msAxisNormalizePoints(&proj, 1, &(ext.minx), &(ext.miny));  | 
2498  | 0  |           msAxisNormalizePoints(&proj, 1, &(ext.maxx), &(ext.maxy));  | 
2499  | 0  |         }  | 
2500  | 0  |       }  | 
2501  |  | 
  | 
2502  | 0  |       encoded = msEncodeHTMLEntities(value);  | 
2503  | 0  |       if (msProjIsGeographicCRS(&proj))  | 
2504  | 0  |         msIO_fprintf(stream,  | 
2505  | 0  |                      "%s<BoundingBox %s=\"%s\"\n"  | 
2506  | 0  |                      "%s            minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" "  | 
2507  | 0  |                      "maxy=\"%.6f\"",  | 
2508  | 0  |                      tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS",  | 
2509  | 0  |                      encoded, tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);  | 
2510  | 0  |       else  | 
2511  | 0  |         msIO_fprintf(  | 
2512  | 0  |             stream,  | 
2513  | 0  |             "%s<BoundingBox %s=\"%s\"\n"  | 
2514  | 0  |             "%s            minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"",  | 
2515  | 0  |             tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS", encoded,  | 
2516  | 0  |             tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);  | 
2517  |  | 
  | 
2518  | 0  |       msFree(encoded);  | 
2519  | 0  |       msFreeProjection(&proj);  | 
2520  |  | 
  | 
2521  | 0  |       if ((resx = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resx")) !=  | 
2522  | 0  |               NULL &&  | 
2523  | 0  |           (resy = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resy")) !=  | 
2524  | 0  |               NULL) { | 
2525  | 0  |         encoded_resx = msEncodeHTMLEntities(resx);  | 
2526  | 0  |         encoded_resy = msEncodeHTMLEntities(resy);  | 
2527  | 0  |         msIO_fprintf(stream, "\n%s            resx=\"%s\" resy=\"%s\"",  | 
2528  | 0  |                      tabspace, encoded_resx, encoded_resy);  | 
2529  | 0  |         msFree(encoded_resx);  | 
2530  | 0  |         msFree(encoded_resy);  | 
2531  | 0  |       }  | 
2532  |  | 
  | 
2533  | 0  |       msIO_fprintf(stream, " />\n");  | 
2534  | 0  |     }  | 
2535  | 0  |   }  | 
2536  | 0  |   msFreeCharArray(epsgs, num_epsgs);  | 
2537  | 0  | }  | 
2538  |  |  | 
2539  |  | /*  | 
2540  |  | ** Print the contact information  | 
2541  |  | */  | 
2542  |  | void msOWSPrintContactInfo(FILE *stream, const char *tabspace, int nVersion,  | 
2543  | 0  |                            hashTableObj *metadata, const char *namespaces) { | 
2544  |  |   /* contact information is a required element in 1.0.7 but the */  | 
2545  |  |   /* sub-elements such as ContactPersonPrimary, etc. are not! */  | 
2546  |  |   /* In 1.1.0, ContactInformation becomes optional. */  | 
2547  | 0  |   if (nVersion > OWS_1_0_0) { | 
2548  | 0  |     msIO_fprintf(stream, "%s<ContactInformation>\n", tabspace);  | 
2549  |  |  | 
2550  |  |     /* ContactPersonPrimary is optional, but when present then all its  */  | 
2551  |  |     /* sub-elements are mandatory */  | 
2552  |  | 
  | 
2553  | 0  |     if (msOWSLookupMetadata(metadata, namespaces, "contactperson") ||  | 
2554  | 0  |         msOWSLookupMetadata(metadata, namespaces, "contactorganization")) { | 
2555  | 0  |       msIO_fprintf(stream, "%s  <ContactPersonPrimary>\n", tabspace);  | 
2556  |  | 
  | 
2557  | 0  |       msOWSPrintEncodeMetadata(  | 
2558  | 0  |           stream, metadata, namespaces, "contactperson", OWS_WARN,  | 
2559  | 0  |           "      <ContactPerson>%s</ContactPerson>\n", NULL);  | 
2560  | 0  |       msOWSPrintEncodeMetadata(  | 
2561  | 0  |           stream, metadata, namespaces, "contactorganization", OWS_WARN,  | 
2562  | 0  |           "      <ContactOrganization>%s</ContactOrganization>\n", NULL);  | 
2563  | 0  |       msIO_fprintf(stream, "%s  </ContactPersonPrimary>\n", tabspace);  | 
2564  | 0  |     }  | 
2565  |  | 
  | 
2566  | 0  |     if (msOWSLookupMetadata(metadata, namespaces, "contactposition")) { | 
2567  | 0  |       msOWSPrintEncodeMetadata(  | 
2568  | 0  |           stream, metadata, namespaces, "contactposition", OWS_NOERR,  | 
2569  | 0  |           "      <ContactPosition>%s</ContactPosition>\n", NULL);  | 
2570  | 0  |     }  | 
2571  |  |  | 
2572  |  |     /* ContactAddress is optional, but when present then all its  */  | 
2573  |  |     /* sub-elements are mandatory */  | 
2574  | 0  |     if (msOWSLookupMetadata(metadata, namespaces, "addresstype") ||  | 
2575  | 0  |         msOWSLookupMetadata(metadata, namespaces, "address") ||  | 
2576  | 0  |         msOWSLookupMetadata(metadata, namespaces, "city") ||  | 
2577  | 0  |         msOWSLookupMetadata(metadata, namespaces, "stateorprovince") ||  | 
2578  | 0  |         msOWSLookupMetadata(metadata, namespaces, "postcode") ||  | 
2579  | 0  |         msOWSLookupMetadata(metadata, namespaces, "country")) { | 
2580  | 0  |       msIO_fprintf(stream, "%s  <ContactAddress>\n", tabspace);  | 
2581  |  | 
  | 
2582  | 0  |       msOWSPrintEncodeMetadata(stream, metadata, namespaces, "addresstype",  | 
2583  | 0  |                                OWS_WARN,  | 
2584  | 0  |                                "        <AddressType>%s</AddressType>\n", NULL);  | 
2585  | 0  |       msOWSPrintEncodeMetadata(stream, metadata, namespaces, "address",  | 
2586  | 0  |                                OWS_WARN, "        <Address>%s</Address>\n",  | 
2587  | 0  |                                NULL);  | 
2588  | 0  |       msOWSPrintEncodeMetadata(stream, metadata, namespaces, "city", OWS_WARN,  | 
2589  | 0  |                                "        <City>%s</City>\n", NULL);  | 
2590  | 0  |       msOWSPrintEncodeMetadata(  | 
2591  | 0  |           stream, metadata, namespaces, "stateorprovince", OWS_WARN,  | 
2592  | 0  |           "        <StateOrProvince>%s</StateOrProvince>\n", NULL);  | 
2593  | 0  |       msOWSPrintEncodeMetadata(stream, metadata, namespaces, "postcode",  | 
2594  | 0  |                                OWS_WARN, "        <PostCode>%s</PostCode>\n",  | 
2595  | 0  |                                NULL);  | 
2596  | 0  |       msOWSPrintEncodeMetadata(stream, metadata, namespaces, "country",  | 
2597  | 0  |                                OWS_WARN, "        <Country>%s</Country>\n",  | 
2598  | 0  |                                NULL);  | 
2599  | 0  |       msIO_fprintf(stream, "%s  </ContactAddress>\n", tabspace);  | 
2600  | 0  |     }  | 
2601  |  | 
  | 
2602  | 0  |     if (msOWSLookupMetadata(metadata, namespaces, "contactvoicetelephone")) { | 
2603  | 0  |       msOWSPrintEncodeMetadata(  | 
2604  | 0  |           stream, metadata, namespaces, "contactvoicetelephone", OWS_NOERR,  | 
2605  | 0  |           "      <ContactVoiceTelephone>%s</ContactVoiceTelephone>\n", NULL);  | 
2606  | 0  |     }  | 
2607  |  | 
  | 
2608  | 0  |     if (msOWSLookupMetadata(metadata, namespaces,  | 
2609  | 0  |                             "contactfacsimiletelephone")) { | 
2610  | 0  |       msOWSPrintEncodeMetadata(  | 
2611  | 0  |           stream, metadata, namespaces, "contactfacsimiletelephone", OWS_NOERR,  | 
2612  | 0  |           "      <ContactFacsimileTelephone>%s</ContactFacsimileTelephone>\n",  | 
2613  | 0  |           NULL);  | 
2614  | 0  |     }  | 
2615  |  | 
  | 
2616  | 0  |     if (msOWSLookupMetadata(metadata, namespaces,  | 
2617  | 0  |                             "contactelectronicmailaddress")) { | 
2618  | 0  |       msOWSPrintEncodeMetadata(  | 
2619  | 0  |           stream, metadata, namespaces, "contactelectronicmailaddress",  | 
2620  | 0  |           OWS_NOERR,  | 
2621  | 0  |           "  <ContactElectronicMailAddress>%s</ContactElectronicMailAddress>\n",  | 
2622  | 0  |           NULL);  | 
2623  | 0  |     }  | 
2624  | 0  |     msIO_fprintf(stream, "%s</ContactInformation>\n", tabspace);  | 
2625  | 0  |   }  | 
2626  | 0  | }  | 
2627  |  |  | 
2628  |  | /*  | 
2629  |  | ** msOWSGetLayerExtent()  | 
2630  |  | **  | 
2631  |  | ** Try to establish layer extent, first looking for "ows_extent" metadata, and  | 
2632  |  | ** if not found then call msLayerGetExtent() which will lookup the  | 
2633  |  | ** layer->extent member, and if not found will open layer to read extent.  | 
2634  |  | **  | 
2635  |  | */  | 
2636  |  | int msOWSGetLayerExtent(mapObj *map, layerObj *lp, const char *namespaces,  | 
2637  | 0  |                         rectObj *ext) { | 
2638  | 0  |   (void)map;  | 
2639  | 0  |   const char *value;  | 
2640  |  | 
  | 
2641  | 0  |   if ((value = msOWSLookupMetadata(&(lp->metadata), namespaces, "extent")) !=  | 
2642  | 0  |       NULL) { | 
2643  | 0  |     char **tokens;  | 
2644  | 0  |     int n;  | 
2645  |  | 
  | 
2646  | 0  |     tokens = msStringSplit(value, ' ', &n);  | 
2647  | 0  |     if (tokens == NULL || n != 4) { | 
2648  | 0  |       msSetError(MS_WMSERR, "Wrong number of arguments for EXTENT metadata.",  | 
2649  | 0  |                  "msOWSGetLayerExtent()");  | 
2650  | 0  |       return MS_FAILURE;  | 
2651  | 0  |     }  | 
2652  | 0  |     ext->minx = atof(tokens[0]);  | 
2653  | 0  |     ext->miny = atof(tokens[1]);  | 
2654  | 0  |     ext->maxx = atof(tokens[2]);  | 
2655  | 0  |     ext->maxy = atof(tokens[3]);  | 
2656  |  | 
  | 
2657  | 0  |     msFreeCharArray(tokens, n);  | 
2658  | 0  |     return MS_SUCCESS;  | 
2659  | 0  |   } else { | 
2660  | 0  |     return msLayerGetExtent(lp, ext);  | 
2661  | 0  |   }  | 
2662  |  |  | 
2663  | 0  |   return MS_FAILURE;  | 
2664  | 0  | }  | 
2665  |  |  | 
2666  |  | /**********************************************************************  | 
2667  |  |  *                          msOWSExecuteRequests()  | 
2668  |  |  *  | 
2669  |  |  * Execute a number of WFS/WMS HTTP requests in parallel, and then  | 
2670  |  |  * update layerObj information with the result of the requests.  | 
2671  |  |  **********************************************************************/  | 
2672  |  | int msOWSExecuteRequests(httpRequestObj *pasReqInfo, int numRequests,  | 
2673  | 0  |                          mapObj *map, int bCheckLocalCache) { | 
2674  | 0  |   int nStatus, iReq;  | 
2675  |  |  | 
2676  |  |   /* Execute requests */  | 
2677  |  | #if defined(USE_CURL)  | 
2678  |  |   nStatus = msHTTPExecuteRequests(pasReqInfo, numRequests, bCheckLocalCache);  | 
2679  |  | #else  | 
2680  | 0  |   msSetError(MS_WMSERR,  | 
2681  | 0  |              "msOWSExecuteRequests() called apparently without libcurl "  | 
2682  | 0  |              "configured, msHTTPExecuteRequests() not available.",  | 
2683  | 0  |              "msOWSExecuteRequests()");  | 
2684  | 0  |   return MS_FAILURE;  | 
2685  | 0  | #endif  | 
2686  |  |  | 
2687  |  |   /* Scan list of layers and call the handler for each layer type to */  | 
2688  |  |   /* pass them the request results. */  | 
2689  | 0  |   for (iReq = 0; iReq < numRequests; iReq++) { | 
2690  | 0  |     if (pasReqInfo[iReq].nLayerId >= 0 &&  | 
2691  | 0  |         pasReqInfo[iReq].nLayerId < map->numlayers) { | 
2692  | 0  |       layerObj *lp;  | 
2693  |  | 
  | 
2694  | 0  |       lp = GET_LAYER(map, pasReqInfo[iReq].nLayerId);  | 
2695  |  | 
  | 
2696  | 0  |       if (lp->connectiontype == MS_WFS)  | 
2697  | 0  |         msWFSUpdateRequestInfo(lp, &(pasReqInfo[iReq]));  | 
2698  | 0  |     }  | 
2699  | 0  |   }  | 
2700  |  | 
  | 
2701  | 0  |   return nStatus;  | 
2702  | 0  | }  | 
2703  |  |  | 
2704  |  | /**********************************************************************  | 
2705  |  |  *                          msOWSProcessException()  | 
2706  |  |  *  | 
2707  |  |  **********************************************************************/  | 
2708  |  | void msOWSProcessException(layerObj *lp, const char *pszFname, int nErrorCode,  | 
2709  | 0  |                            const char *pszFuncName) { | 
2710  | 0  |   FILE *fp;  | 
2711  |  | 
  | 
2712  | 0  |   if ((fp = fopen(pszFname, "r")) != NULL) { | 
2713  | 0  |     char *pszBuf = NULL;  | 
2714  | 0  |     int nBufSize = 0;  | 
2715  | 0  |     char *pszStart, *pszEnd;  | 
2716  |  | 
  | 
2717  | 0  |     fseek(fp, 0, SEEK_END);  | 
2718  | 0  |     nBufSize = ftell(fp);  | 
2719  | 0  |     if (nBufSize < 0) { | 
2720  | 0  |       msSetError(MS_IOERR, NULL, "msOWSProcessException()");  | 
2721  | 0  |       fclose(fp);  | 
2722  | 0  |       return;  | 
2723  | 0  |     }  | 
2724  | 0  |     rewind(fp);  | 
2725  | 0  |     pszBuf = (char *)malloc((nBufSize + 1) * sizeof(char));  | 
2726  | 0  |     if (pszBuf == NULL) { | 
2727  | 0  |       msSetError(MS_MEMERR, NULL, "msOWSProcessException()");  | 
2728  | 0  |       fclose(fp);  | 
2729  | 0  |       return;  | 
2730  | 0  |     }  | 
2731  |  |  | 
2732  | 0  |     if ((int)fread(pszBuf, 1, nBufSize, fp) != nBufSize) { | 
2733  | 0  |       msSetError(MS_IOERR, NULL, "msOWSProcessException()");  | 
2734  | 0  |       free(pszBuf);  | 
2735  | 0  |       fclose(fp);  | 
2736  | 0  |       return;  | 
2737  | 0  |     }  | 
2738  |  |  | 
2739  | 0  |     pszBuf[nBufSize] = '\0';  | 
2740  |  |  | 
2741  |  |     /* OK, got the data in the buffer.  Look for the <Message> tags */  | 
2742  | 0  |     if ((strstr(pszBuf, "<WFS_Exception>") && /* WFS style */  | 
2743  | 0  |          (pszStart = strstr(pszBuf, "<Message>")) &&  | 
2744  | 0  |          (pszEnd = strstr(pszStart, "</Message>"))) ||  | 
2745  | 0  |         (strstr(pszBuf, "<ServiceExceptionReport>") && /* WMS style */  | 
2746  | 0  |          (pszStart = strstr(pszBuf, "<ServiceException>")) &&  | 
2747  | 0  |          (pszEnd = strstr(pszStart, "</ServiceException>")))) { | 
2748  | 0  |       pszStart = strchr(pszStart, '>') + 1;  | 
2749  | 0  |       *pszEnd = '\0';  | 
2750  | 0  |       msSetError(nErrorCode, "Got Remote Server Exception for layer %s: %s",  | 
2751  | 0  |                  pszFuncName, lp->name ? lp->name : "(null)", pszStart);  | 
2752  | 0  |     } else { | 
2753  | 0  |       msSetError(  | 
2754  | 0  |           MS_WFSCONNERR,  | 
2755  | 0  |           "Unable to parse Remote Server Exception Message for layer %s.",  | 
2756  | 0  |           pszFuncName, lp->name ? lp->name : "(null)");  | 
2757  | 0  |     }  | 
2758  |  | 
  | 
2759  | 0  |     free(pszBuf);  | 
2760  | 0  |     fclose(fp);  | 
2761  | 0  |   }  | 
2762  | 0  | }  | 
2763  |  |  | 
2764  |  | /**********************************************************************  | 
2765  |  |  *                          msOWSBuildURLFilename()  | 
2766  |  |  *  | 
2767  |  |  * Build a unique filename for this URL to use in caching remote server  | 
2768  |  |  * requests.  Slashes and illegal characters will be turned into '_'  | 
2769  |  |  *  | 
2770  |  |  * Returns a newly allocated buffer that should be freed by the caller or  | 
2771  |  |  * NULL in case of error.  | 
2772  |  |  **********************************************************************/  | 
2773  |  | char *msOWSBuildURLFilename(const char *pszPath, const char *pszURL,  | 
2774  | 0  |                             const char *pszExt) { | 
2775  | 0  |   char *pszBuf, *pszPtr;  | 
2776  | 0  |   int i;  | 
2777  | 0  |   size_t nBufLen = 0;  | 
2778  |  | 
  | 
2779  | 0  |   nBufLen = strlen(pszURL) + strlen(pszExt) + 2;  | 
2780  | 0  |   if (pszPath)  | 
2781  | 0  |     nBufLen += (strlen(pszPath) + 1);  | 
2782  |  | 
  | 
2783  | 0  |   pszBuf = (char *)malloc(nBufLen);  | 
2784  | 0  |   if (pszBuf == NULL) { | 
2785  | 0  |     msSetError(MS_MEMERR, NULL, "msOWSBuildURLFilename()");  | 
2786  | 0  |     return NULL;  | 
2787  | 0  |   }  | 
2788  | 0  |   pszBuf[0] = '\0';  | 
2789  |  | 
  | 
2790  | 0  |   if (pszPath) { | 
2791  |  | #ifdef _WIN32  | 
2792  |  |     if (pszPath[strlen(pszPath) - 1] != '/' &&  | 
2793  |  |         pszPath[strlen(pszPath) - 1] != '\\')  | 
2794  |  |       snprintf(pszBuf, nBufLen, "%s\\", pszPath);  | 
2795  |  |     else  | 
2796  |  |       snprintf(pszBuf, nBufLen, "%s", pszPath);  | 
2797  |  | #else  | 
2798  | 0  |     if (pszPath[strlen(pszPath) - 1] != '/')  | 
2799  | 0  |       snprintf(pszBuf, nBufLen, "%s/", pszPath);  | 
2800  | 0  |     else  | 
2801  | 0  |       snprintf(pszBuf, nBufLen, "%s", pszPath);  | 
2802  | 0  | #endif  | 
2803  | 0  |   }  | 
2804  |  | 
  | 
2805  | 0  |   pszPtr = pszBuf + strlen(pszBuf);  | 
2806  |  | 
  | 
2807  | 0  |   for (i = 0; pszURL[i] != '\0'; i++) { | 
2808  | 0  |     if (isalnum(pszURL[i]))  | 
2809  | 0  |       *pszPtr = pszURL[i];  | 
2810  | 0  |     else  | 
2811  | 0  |       *pszPtr = '_';  | 
2812  | 0  |     pszPtr++;  | 
2813  | 0  |   }  | 
2814  |  | 
  | 
2815  | 0  |   strlcpy(pszPtr, pszExt, nBufLen);  | 
2816  |  | 
  | 
2817  | 0  |   return pszBuf;  | 
2818  | 0  | }  | 
2819  |  |  | 
2820  |  | /*  | 
2821  |  | ** msOWSGetProjURN()  | 
2822  |  | **  | 
2823  |  | ** Fetch an OGC URN for this layer or map.  Similar to msOWSGetEPSGProj()  | 
2824  |  | ** but returns the result in the form "urn:ogc:def:crs:EPSG::27700".  | 
2825  |  | ** The returned buffer is dynamically allocated, and must be freed by the  | 
2826  |  | ** caller.  | 
2827  |  | */  | 
2828  |  | char *msOWSGetProjURN(projectionObj *proj, hashTableObj *metadata,  | 
2829  | 0  |                       const char *namespaces, int bReturnOnlyFirstOne) { | 
2830  | 0  |   char *result;  | 
2831  | 0  |   char **tokens;  | 
2832  | 0  |   int numtokens, i;  | 
2833  | 0  |   char *oldStyle = NULL;  | 
2834  |  | 
  | 
2835  | 0  |   msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);  | 
2836  |  | 
  | 
2837  | 0  |   if (oldStyle == NULL || strncmp(oldStyle, "CRS:", 4) == 0 ||  | 
2838  | 0  |       strncmp(oldStyle, "AUTO:", 5) == 0 ||  | 
2839  | 0  |       strncmp(oldStyle, "AUTO2:", 6) == 0) { | 
2840  | 0  |     msFree(oldStyle);  | 
2841  | 0  |     return NULL;  | 
2842  | 0  |   }  | 
2843  |  |  | 
2844  | 0  |   result = msStrdup(""); | 
2845  |  | 
  | 
2846  | 0  |   tokens = msStringSplit(oldStyle, ' ', &numtokens);  | 
2847  | 0  |   msFree(oldStyle);  | 
2848  | 0  |   for (i = 0; tokens != NULL && i < numtokens; i++) { | 
2849  | 0  |     char urn[100];  | 
2850  | 0  |     char *colon = strchr(tokens[i], ':');  | 
2851  |  | 
  | 
2852  | 0  |     if (colon != NULL && strchr(colon + 1, ':') == NULL) { | 
2853  | 0  |       *colon = 0;  | 
2854  | 0  |       snprintf(urn, sizeof(urn), "urn:ogc:def:crs:%s::%s", tokens[i],  | 
2855  | 0  |                colon + 1);  | 
2856  | 0  |     } else if (strcasecmp(tokens[i], "imageCRS") == 0)  | 
2857  | 0  |       snprintf(urn, sizeof(urn), "urn:ogc:def:crs:OGC::imageCRS");  | 
2858  | 0  |     else if (strncmp(tokens[i], "urn:ogc:def:crs:", 16) == 0) { | 
2859  | 0  |       strlcpy(urn, tokens[i], sizeof(urn));  | 
2860  | 0  |     } else { | 
2861  | 0  |       strlcpy(urn, "", sizeof(urn));  | 
2862  | 0  |     }  | 
2863  |  | 
  | 
2864  | 0  |     if (strlen(urn) > 0) { | 
2865  | 0  |       const size_t bufferSize = strlen(result) + strlen(urn) + 2;  | 
2866  | 0  |       result = (char *)msSmallRealloc(result, bufferSize);  | 
2867  |  | 
  | 
2868  | 0  |       if (strlen(result) > 0)  | 
2869  | 0  |         strlcat(result, " ", bufferSize);  | 
2870  | 0  |       strlcat(result, urn, bufferSize);  | 
2871  | 0  |     } else { | 
2872  | 0  |       msDebug("msOWSGetProjURN(): Failed to process SRS '%s', ignored.", | 
2873  | 0  |               tokens[i]);  | 
2874  | 0  |     }  | 
2875  | 0  |   }  | 
2876  |  | 
  | 
2877  | 0  |   msFreeCharArray(tokens, numtokens);  | 
2878  |  | 
  | 
2879  | 0  |   if (strlen(result) == 0) { | 
2880  | 0  |     msFree(result);  | 
2881  | 0  |     return NULL;  | 
2882  | 0  |   } else  | 
2883  | 0  |     return result;  | 
2884  | 0  | }  | 
2885  |  |  | 
2886  |  | /*  | 
2887  |  | ** msOWSGetProjURI()  | 
2888  |  | **  | 
2889  |  | ** Fetch an OGC URI for this layer or map.  Similar to msOWSGetEPSGProj()  | 
2890  |  | ** but returns the result in the form  | 
2891  |  | *"http://www.opengis.net/def/crs/EPSG/0/27700".  | 
2892  |  | ** The returned buffer is dynamically allocated, and must be freed by the  | 
2893  |  | ** caller.  | 
2894  |  | */  | 
2895  |  | char *msOWSGetProjURI(projectionObj *proj, hashTableObj *metadata,  | 
2896  | 0  |                       const char *namespaces, int bReturnOnlyFirstOne) { | 
2897  | 0  |   char *result;  | 
2898  | 0  |   char **tokens;  | 
2899  | 0  |   int numtokens, i;  | 
2900  | 0  |   char *oldStyle = NULL;  | 
2901  |  | 
  | 
2902  | 0  |   msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);  | 
2903  |  | 
  | 
2904  | 0  |   if (oldStyle == NULL || !EQUALN(oldStyle, "EPSG:", 5)) { | 
2905  | 0  |     msFree(oldStyle); // avoid leak  | 
2906  | 0  |     return NULL;  | 
2907  | 0  |   }  | 
2908  |  |  | 
2909  | 0  |   result = msStrdup(""); | 
2910  |  | 
  | 
2911  | 0  |   tokens = msStringSplit(oldStyle, ' ', &numtokens);  | 
2912  | 0  |   msFree(oldStyle);  | 
2913  | 0  |   for (i = 0; tokens != NULL && i < numtokens; i++) { | 
2914  | 0  |     char urn[100];  | 
2915  |  | 
  | 
2916  | 0  |     if (strncmp(tokens[i], "EPSG:", 5) == 0)  | 
2917  | 0  |       snprintf(urn, sizeof(urn), "http://www.opengis.net/def/crs/EPSG/0/%s",  | 
2918  | 0  |                tokens[i] + 5);  | 
2919  | 0  |     else if (strcasecmp(tokens[i], "imageCRS") == 0)  | 
2920  | 0  |       snprintf(urn, sizeof(urn),  | 
2921  | 0  |                "http://www.opengis.net/def/crs/OGC/0/imageCRS");  | 
2922  | 0  |     else if (strncmp(tokens[i], "http://www.opengis.net/def/crs/", 16) == 0)  | 
2923  | 0  |       snprintf(urn, sizeof(urn), "%s", tokens[i]);  | 
2924  | 0  |     else  | 
2925  | 0  |       strlcpy(urn, "", sizeof(urn));  | 
2926  |  | 
  | 
2927  | 0  |     if (strlen(urn) > 0) { | 
2928  | 0  |       result = (char *)msSmallRealloc(result, strlen(result) + strlen(urn) + 2);  | 
2929  |  | 
  | 
2930  | 0  |       if (strlen(result) > 0)  | 
2931  | 0  |         strcat(result, " ");  | 
2932  | 0  |       strcat(result, urn);  | 
2933  | 0  |     } else { | 
2934  | 0  |       msDebug("msOWSGetProjURI(): Failed to process SRS '%s', ignored.", | 
2935  | 0  |               tokens[i]);  | 
2936  | 0  |     }  | 
2937  | 0  |   }  | 
2938  |  | 
  | 
2939  | 0  |   msFreeCharArray(tokens, numtokens);  | 
2940  |  | 
  | 
2941  | 0  |   if (strlen(result) == 0) { | 
2942  | 0  |     msFree(result);  | 
2943  | 0  |     return NULL;  | 
2944  | 0  |   } else  | 
2945  | 0  |     return result;  | 
2946  | 0  | }  | 
2947  |  |  | 
2948  |  | /*  | 
2949  |  | ** msOWSGetDimensionInfo()  | 
2950  |  | **  | 
2951  |  | ** Extract dimension information from a layer's metadata  | 
2952  |  | **  | 
2953  |  | ** Before 4.9, only the time dimension was support. With the addition of  | 
2954  |  | ** Web Map Context 1.1.0, we need to support every dimension types.  | 
2955  |  | ** This function get the dimension information from special metadata in  | 
2956  |  | ** the layer, but can also return default values for the time dimension.  | 
2957  |  | **  | 
2958  |  | */  | 
2959  |  | void msOWSGetDimensionInfo(layerObj *layer, const char *pszDimension,  | 
2960  |  |                            const char **papszDimUserValue,  | 
2961  |  |                            const char **papszDimUnits,  | 
2962  |  |                            const char **papszDimDefault,  | 
2963  |  |                            const char **papszDimNearValue,  | 
2964  |  |                            const char **papszDimUnitSymbol,  | 
2965  | 0  |                            const char **papszDimMultiValue) { | 
2966  | 0  |   char *pszDimensionItem;  | 
2967  | 0  |   size_t bufferSize = 0;  | 
2968  |  | 
  | 
2969  | 0  |   if (pszDimension == NULL || layer == NULL)  | 
2970  | 0  |     return;  | 
2971  |  |  | 
2972  | 0  |   bufferSize = strlen(pszDimension) + 50;  | 
2973  | 0  |   pszDimensionItem = (char *)malloc(bufferSize);  | 
2974  |  |  | 
2975  |  |   /* units (mandatory in map context) */  | 
2976  | 0  |   if (papszDimUnits != NULL) { | 
2977  | 0  |     snprintf(pszDimensionItem, bufferSize, "dimension_%s_units", pszDimension);  | 
2978  | 0  |     *papszDimUnits =  | 
2979  | 0  |         msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);  | 
2980  | 0  |   }  | 
2981  |  |   /* unitSymbol (mandatory in map context) */  | 
2982  | 0  |   if (papszDimUnitSymbol != NULL) { | 
2983  | 0  |     snprintf(pszDimensionItem, bufferSize, "dimension_%s_unitsymbol",  | 
2984  | 0  |              pszDimension);  | 
2985  | 0  |     *papszDimUnitSymbol =  | 
2986  | 0  |         msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);  | 
2987  | 0  |   }  | 
2988  |  |   /* userValue (mandatory in map context) */  | 
2989  | 0  |   if (papszDimUserValue != NULL) { | 
2990  | 0  |     snprintf(pszDimensionItem, bufferSize, "dimension_%s_uservalue",  | 
2991  | 0  |              pszDimension);  | 
2992  | 0  |     *papszDimUserValue =  | 
2993  | 0  |         msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);  | 
2994  | 0  |   }  | 
2995  |  |   /* default */  | 
2996  | 0  |   if (papszDimDefault != NULL) { | 
2997  | 0  |     snprintf(pszDimensionItem, bufferSize, "dimension_%s_default",  | 
2998  | 0  |              pszDimension);  | 
2999  | 0  |     *papszDimDefault =  | 
3000  | 0  |         msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);  | 
3001  | 0  |   }  | 
3002  |  |   /* multipleValues */  | 
3003  | 0  |   if (papszDimMultiValue != NULL) { | 
3004  | 0  |     snprintf(pszDimensionItem, bufferSize, "dimension_%s_multiplevalues",  | 
3005  | 0  |              pszDimension);  | 
3006  | 0  |     *papszDimMultiValue =  | 
3007  | 0  |         msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);  | 
3008  | 0  |   }  | 
3009  |  |   /* nearestValue */  | 
3010  | 0  |   if (papszDimNearValue != NULL) { | 
3011  | 0  |     snprintf(pszDimensionItem, bufferSize, "dimension_%s_nearestvalue",  | 
3012  | 0  |              pszDimension);  | 
3013  | 0  |     *papszDimNearValue =  | 
3014  | 0  |         msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);  | 
3015  | 0  |   }  | 
3016  |  |  | 
3017  |  |   /* Use default time value if necessary */  | 
3018  | 0  |   if (strcasecmp(pszDimension, "time") == 0) { | 
3019  | 0  |     if (papszDimUserValue != NULL && *papszDimUserValue == NULL)  | 
3020  | 0  |       *papszDimUserValue =  | 
3021  | 0  |           msOWSLookupMetadata(&(layer->metadata), "MO", "time");  | 
3022  | 0  |     if (papszDimDefault != NULL && *papszDimDefault == NULL)  | 
3023  | 0  |       *papszDimDefault =  | 
3024  | 0  |           msOWSLookupMetadata(&(layer->metadata), "MO", "timedefault");  | 
3025  | 0  |     if (papszDimUnits != NULL && *papszDimUnits == NULL)  | 
3026  | 0  |       *papszDimUnits = "ISO8601";  | 
3027  | 0  |     if (papszDimUnitSymbol != NULL && *papszDimUnitSymbol == NULL)  | 
3028  | 0  |       *papszDimUnitSymbol = "t";  | 
3029  | 0  |     if (papszDimNearValue != NULL && *papszDimNearValue == NULL)  | 
3030  | 0  |       *papszDimNearValue = "0";  | 
3031  | 0  |   }  | 
3032  |  | 
  | 
3033  | 0  |   free(pszDimensionItem);  | 
3034  |  | 
  | 
3035  | 0  |   return;  | 
3036  | 0  | }  | 
3037  |  |  | 
3038  |  | /**  | 
3039  |  |  * msOWSNegotiateUpdateSequence()  | 
3040  |  |  *  | 
3041  |  |  * returns the updateSequence value for an OWS  | 
3042  |  |  *  | 
3043  |  |  * @param requested_updatesequence the updatesequence passed by the client  | 
3044  |  |  * @param updatesequence the updatesequence set by the server  | 
3045  |  |  *  | 
3046  |  |  * @return result of comparison (-1, 0, 1)  | 
3047  |  |  * -1: lower / higher OR values not set by client or server  | 
3048  |  |  *  1: higher / lower  | 
3049  |  |  *  0: equal  | 
3050  |  |  */  | 
3051  |  |  | 
3052  |  | int msOWSNegotiateUpdateSequence(const char *requested_updatesequence,  | 
3053  | 0  |                                  const char *updatesequence) { | 
3054  | 0  |   int valtype1 = 1; /* default datatype for updatesequence passed by client */  | 
3055  | 0  |   int valtype2 = 1; /* default datatype for updatesequence set by server */  | 
3056  | 0  |   struct tm tm_requested_updatesequence, tm_updatesequence;  | 
3057  |  |  | 
3058  |  |   /* if not specified by client, or set by server,  | 
3059  |  |      server responds with latest Capabilities XML */  | 
3060  | 0  |   if (!requested_updatesequence || !updatesequence)  | 
3061  | 0  |     return -1;  | 
3062  |  |  | 
3063  |  |   /* test to see if server value is an integer (1), string (2) or timestamp (3)  | 
3064  |  |    */  | 
3065  | 0  |   if (msStringIsInteger(updatesequence) == MS_FAILURE)  | 
3066  | 0  |     valtype1 = 2;  | 
3067  |  | 
  | 
3068  | 0  |   if (valtype1 == 2) { /* test if timestamp */ | 
3069  | 0  |     msTimeInit(&tm_updatesequence);  | 
3070  | 0  |     if (msParseTime(updatesequence, &tm_updatesequence) == MS_TRUE)  | 
3071  | 0  |       valtype1 = 3;  | 
3072  | 0  |     msResetErrorList();  | 
3073  | 0  |   }  | 
3074  |  |  | 
3075  |  |   /* test to see if client value is an integer (1), string (2) or timestamp (3)  | 
3076  |  |    */  | 
3077  | 0  |   if (msStringIsInteger(requested_updatesequence) == MS_FAILURE)  | 
3078  | 0  |     valtype2 = 2;  | 
3079  |  | 
  | 
3080  | 0  |   if (valtype2 == 2) { /* test if timestamp */ | 
3081  | 0  |     msTimeInit(&tm_requested_updatesequence);  | 
3082  | 0  |     if (msParseTime(requested_updatesequence, &tm_requested_updatesequence) ==  | 
3083  | 0  |         MS_TRUE)  | 
3084  | 0  |       valtype2 = 3;  | 
3085  | 0  |     msResetErrorList();  | 
3086  | 0  |   }  | 
3087  |  |  | 
3088  |  |   /* if the datatypes do not match, do not compare, */  | 
3089  | 0  |   if (valtype1 != valtype2)  | 
3090  | 0  |     return -1;  | 
3091  |  |  | 
3092  | 0  |   if (valtype1 == 1) { /* integer */ | 
3093  | 0  |     const int requested_updatesequence_i = atoi(requested_updatesequence);  | 
3094  | 0  |     const int updatesequence_i = atoi(updatesequence);  | 
3095  | 0  |     if (requested_updatesequence_i < updatesequence_i)  | 
3096  | 0  |       return -1;  | 
3097  |  |  | 
3098  | 0  |     if (requested_updatesequence_i > updatesequence_i)  | 
3099  | 0  |       return 1;  | 
3100  |  |  | 
3101  | 0  |     return 0;  | 
3102  | 0  |   }  | 
3103  |  |  | 
3104  | 0  |   if (valtype1 == 2) /* string */  | 
3105  | 0  |     return strcasecmp(requested_updatesequence, updatesequence);  | 
3106  |  |  | 
3107  | 0  |   assert(valtype1 == 3); /* timestamp */  | 
3108  |  |   /* compare timestamps */  | 
3109  | 0  |   return msDateCompare(&tm_requested_updatesequence, &tm_updatesequence) +  | 
3110  | 0  |          msTimeCompare(&tm_requested_updatesequence, &tm_updatesequence);  | 
3111  | 0  | }  | 
3112  |  |  | 
3113  |  | /************************************************************************/  | 
3114  |  | /*                         msOwsIsOutputFormatValid                     */  | 
3115  |  | /*                                                                      */  | 
3116  |  | /*      Utility function to parse a comma separated list in a            */  | 
3117  |  | /*      metedata object and select and outputformat.                    */  | 
3118  |  | /************************************************************************/  | 
3119  |  | outputFormatObj *msOwsIsOutputFormatValid(mapObj *map, const char *format,  | 
3120  |  |                                           hashTableObj *metadata,  | 
3121  |  |                                           const char *namespaces,  | 
3122  | 0  |                                           const char *name) { | 
3123  | 0  |   char **tokens = NULL;  | 
3124  | 0  |   int i, n;  | 
3125  | 0  |   outputFormatObj *psFormat = NULL;  | 
3126  | 0  |   const char *format_list = NULL;  | 
3127  |  | 
  | 
3128  | 0  |   if (map && format && metadata && namespaces && name) { | 
3129  | 0  |     msApplyDefaultOutputFormats(map);  | 
3130  | 0  |     format_list = msOWSLookupMetadata(metadata, namespaces, name);  | 
3131  | 0  |     n = 0;  | 
3132  | 0  |     if (format_list)  | 
3133  | 0  |       tokens = msStringSplit(format_list, ',', &n);  | 
3134  |  | 
  | 
3135  | 0  |     if (tokens && n > 0) { | 
3136  | 0  |       for (i = 0; i < n; i++) { | 
3137  | 0  |         int iFormat = msGetOutputFormatIndex(map, tokens[i]);  | 
3138  | 0  |         const char *mimetype;  | 
3139  | 0  |         if (iFormat == -1)  | 
3140  | 0  |           continue;  | 
3141  |  |  | 
3142  | 0  |         mimetype = map->outputformatlist[iFormat]->mimetype;  | 
3143  |  | 
  | 
3144  | 0  |         msStringTrim(tokens[i]);  | 
3145  | 0  |         if (strcasecmp(tokens[i], format) == 0)  | 
3146  | 0  |           break;  | 
3147  | 0  |         if (mimetype && strcasecmp(mimetype, format) == 0)  | 
3148  | 0  |           break;  | 
3149  | 0  |       }  | 
3150  | 0  |       if (i < n)  | 
3151  | 0  |         psFormat = msSelectOutputFormat(map, format);  | 
3152  | 0  |     }  | 
3153  | 0  |     if (tokens)  | 
3154  | 0  |       msFreeCharArray(tokens, n);  | 
3155  | 0  |   }  | 
3156  |  | 
  | 
3157  | 0  |   return psFormat;  | 
3158  | 0  | }  | 
3159  |  |  | 
3160  |  | #endif /* defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined             \  | 
3161  |  |           (USE_WCS_SVR) || defined(USE_SOS_SVR) || defined(USE_WMS_LYR) ||     \  | 
3162  |  |           defined(USE_WFS_LYR) */  |