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