1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  OpenGIS Web Coverage Server (WCS) Implementation.
6  * Author:   Steve Lime and the MapServer team.
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 "maperror.h"
32 #include "mapthread.h"
33 #include "mapows.h"
34 #include <assert.h>
35 
36 
37 
38 #if defined(USE_WCS_SVR)
39 
40 #include "mapwcs.h"
41 
42 #include "maptime.h"
43 #include <time.h>
44 
45 #include "gdal.h"
46 #include "cpl_string.h" /* GDAL string handling */
47 
48 /************************************************************************/
49 /*                    msWCSValidateRangeSetParam()                      */
50 /************************************************************************/
msWCSValidateRangeSetParam(layerObj * lp,char * name,const char * value)51 static int msWCSValidateRangeSetParam(layerObj *lp, char *name, const char *value)
52 {
53   char **allowed_ri_values;
54   char **client_ri_values;
55   int  allowed_count, client_count;
56   int  i_client, i, all_match = 1;
57   char *tmpname = NULL;
58   const char *ri_values_list;
59 
60   if( name == NULL )
61     return MS_FAILURE;
62 
63   /* Fetch the available values list for the rangeset item and tokenize */
64   tmpname = (char *)msSmallMalloc(sizeof(char)*strlen(name) + 10);
65   sprintf(tmpname,"%s_values", name);
66   ri_values_list = msOWSLookupMetadata(&(lp->metadata), "CO", tmpname);
67   msFree( tmpname );
68 
69   if (ri_values_list == NULL)
70     return MS_FAILURE;
71 
72   allowed_ri_values = msStringSplit( ri_values_list, ',', &allowed_count);
73 
74   /* Parse the client value list into tokens. */
75   client_ri_values = msStringSplit( value, ',', &client_count );
76 
77   /* test each client value against the allowed list. */
78 
79   for( i_client = 0; all_match && i_client < client_count; i_client++ ) {
80     for( i = 0;
81          i < allowed_count
82          && strcasecmp(client_ri_values[i_client],
83                        allowed_ri_values[i]) != 0;
84          i++ ) {}
85 
86     if( i == allowed_count )
87       all_match = 0;
88   }
89 
90   msFreeCharArray(allowed_ri_values, allowed_count );
91   msFreeCharArray(client_ri_values, client_count );
92 
93   if (all_match == 0)
94     return MS_FAILURE;
95   else
96     return MS_SUCCESS;
97 }
98 
99 /************************************************************************/
100 /*                    msWCSConvertRangeSetToString()                    */
101 /************************************************************************/
msWCSConvertRangeSetToString(const char * value)102 static char *msWCSConvertRangeSetToString(const char *value)
103 {
104   char **tokens;
105   int numtokens;
106   double min, max, res;
107   double val;
108   char buf1[128], *buf2=NULL;
109 
110   if(strchr(value, '/')) { /* value is min/max/res */
111     tokens = msStringSplit(value, '/', &numtokens);
112     if(tokens==NULL || numtokens != 3) {
113       msFreeCharArray(tokens, numtokens);
114       return NULL; /* not a set of equally spaced intervals */
115     }
116 
117     min = atof(tokens[0]);
118     max = atof(tokens[1]);
119     res = atof(tokens[2]);
120     msFreeCharArray(tokens, numtokens);
121 
122     for(val=min; val<=max; val+=res) {
123       if(val == min)
124         snprintf(buf1, sizeof(buf1), "%g", val);
125       else
126         snprintf(buf1, sizeof(buf1), ",%g", val);
127       buf2 = msStringConcatenate(buf2, buf1);
128     }
129 
130     return buf2;
131   } else
132     return msStrdup(value);
133 }
134 
135 /************************************************************************/
136 /*                           msWCSException()                           */
137 /************************************************************************/
msWCSException(mapObj * map,const char * code,const char * locator,const char * version)138 int msWCSException(mapObj *map, const char *code, const char *locator,
139                    const char *version )
140 {
141   char *pszEncodedVal = NULL;
142   char version_string[OWS_VERSION_MAXLEN];
143 
144   if( version == NULL )
145     version = "1.0.0";
146 
147 #if defined(USE_LIBXML2)
148   if( msOWSParseVersionString(version) >= OWS_2_0_0 )
149     return msWCSException20( map, code, locator, msOWSGetVersionString(msOWSParseVersionString(version), version_string) );
150 #endif
151 
152   if( msOWSParseVersionString(version) >= OWS_1_1_0 )
153     return msWCSException11( map, code, locator, msOWSGetVersionString(msOWSParseVersionString(version), version_string) );
154 
155   msIO_setHeader("Content-Type","application/vnd.ogc.se_xml; charset=UTF-8");
156   msIO_sendHeaders();
157 
158   /* msIO_printf("Content-Type: text/xml%c%c",10,10); */
159 
160   msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
161 
162   msIO_printf("<ServiceExceptionReport version=\"1.2.0\"\n");
163   msIO_printf("xmlns=\"http://www.opengis.net/ogc\" ");
164   msIO_printf("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
165   pszEncodedVal = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
166   msIO_printf("xsi:schemaLocation=\"http://www.opengis.net/ogc %s/wcs/1.0.0/OGC-exception.xsd\">\n",
167               pszEncodedVal);
168   msFree(pszEncodedVal);
169   msIO_printf("  <ServiceException");
170   if (code) {
171     msIO_printf(" code=\"%s\"", code);
172   }
173   if (locator) {
174     msIO_printf(" locator=\"%s\"", locator);
175   }
176   msIO_printf(">");
177   msWriteErrorXML(stdout);
178   msIO_printf("  </ServiceException>\n");
179   msIO_printf("</ServiceExceptionReport>\n");
180 
181   msResetErrorList();
182 
183   return MS_FAILURE;
184 }
185 
186 /************************************************************************/
187 /*                    msWCSPrintRequestCapability()                     */
188 /************************************************************************/
189 
msWCSPrintRequestCapability(const char * version,const char * request_tag,const char * script_url)190 static void msWCSPrintRequestCapability(const char *version, const char *request_tag, const char *script_url)
191 {
192   msIO_printf("    <%s>\n", request_tag);
193 
194   msIO_printf("      <DCPType>\n");
195   msIO_printf("        <HTTP>\n");
196   msIO_printf("          <Get><OnlineResource xlink:type=\"simple\" xlink:href=\"%s\" /></Get>\n", script_url);
197   msIO_printf("        </HTTP>\n");
198   msIO_printf("      </DCPType>\n");
199   msIO_printf("      <DCPType>\n");
200   msIO_printf("        <HTTP>\n");
201   msIO_printf("          <Post><OnlineResource xlink:type=\"simple\" xlink:href=\"%s\" /></Post>\n", script_url);
202   msIO_printf("        </HTTP>\n");
203   msIO_printf("      </DCPType>\n");
204 
205   msIO_printf("    </%s>\n", request_tag);
206 }
207 
208 /************************************************************************/
209 /*                         msWCSCreateParams()                          */
210 /************************************************************************/
msWCSCreateParams()211 static wcsParamsObj *msWCSCreateParams()
212 {
213   wcsParamsObj *params;
214 
215   params = (wcsParamsObj *) calloc(1, sizeof(wcsParamsObj));
216   MS_CHECK_ALLOC(params, sizeof(wcsParamsObj), NULL);
217 
218   return params;
219 }
220 
221 /************************************************************************/
222 /*                          msWCSFreeParams()                           */
223 /************************************************************************/
msWCSFreeParams(wcsParamsObj * params)224 void msWCSFreeParams(wcsParamsObj *params)
225 {
226   if(params) {
227     /* TODO */
228     if(params->version) free(params->version);
229     if(params->updatesequence) free(params->updatesequence);
230     if(params->request) free(params->request);
231     if(params->service) free(params->service);
232     if(params->section) free(params->section);
233     if(params->crs) free(params->crs);
234     if(params->response_crs) free(params->response_crs);
235     if(params->format) free(params->format);
236     if(params->exceptions) free(params->exceptions);
237     if(params->time) free(params->time);
238     if(params->interpolation) free(params->interpolation);
239     CSLDestroy( params->coverages );
240   }
241 }
242 
243 /************************************************************************/
244 /*                       msWCSIsLayerSupported()                        */
245 /************************************************************************/
246 
msWCSIsLayerSupported(layerObj * layer)247 int msWCSIsLayerSupported(layerObj *layer)
248 {
249   /* only raster layers, are elligible to be served via WCS, WMS rasters are not ok */
250   if((layer->type == MS_LAYER_RASTER) && layer->connectiontype != MS_WMS && layer->name != NULL) return MS_TRUE;
251 
252   return MS_FALSE;
253 }
254 
255 /************************************************************************/
256 /*                      msWCSGetRequestParameter()                      */
257 /*                                                                      */
258 /************************************************************************/
259 
msWCSGetRequestParameter(cgiRequestObj * request,char * name)260 const char *msWCSGetRequestParameter(cgiRequestObj *request, char *name)
261 {
262   int i;
263 
264   if(!request || !name) /* nothing to do */
265     return NULL;
266 
267   if(request->NumParams > 0) {
268     for(i=0; i<request->NumParams; i++) {
269       if(strcasecmp(request->ParamNames[i], name) == 0)
270         return request->ParamValues[i];
271     }
272   }
273 
274   return NULL;
275 }
276 
277 /************************************************************************/
278 /*                  msWCSSetDefaultBandsRangeSetInfo()                  */
279 /************************************************************************/
280 
msWCSSetDefaultBandsRangeSetInfo(wcsParamsObj * params,coverageMetadataObj * cm,layerObj * lp)281 void msWCSSetDefaultBandsRangeSetInfo( wcsParamsObj *params,
282                                        coverageMetadataObj *cm,
283                                        layerObj *lp )
284 {
285 
286   /* This function will provide default rangeset information for the "special" */
287   /* "bands" rangeset if it appears in the axes list but has no specifics provided */
288   /* in the metadata.   */
289 
290   const char *value;
291   char *bandlist;
292   size_t bufferSize = 0;
293   int  i;
294 
295   /* Does this item exist in the axes list?  */
296 
297   value = msOWSLookupMetadata(&(lp->metadata), "CO", "rangeset_axes");
298   if( value == NULL )
299     return;
300 
301   value = strstr(value,"bands");
302   if( value == NULL || (value[5] != '\0' && value[5] != ' ') )
303     return;
304 
305   /* Are there any w*s_bands_ metadata already? If so, skip out. */
306   if( msOWSLookupMetadata(&(lp->metadata), "CO", "bands_description") != NULL
307       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_name") != NULL
308       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_label") != NULL
309       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values") != NULL
310       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values_semantic") != NULL
311       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_values_type") != NULL
312       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_rangeitem") != NULL
313       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_semantic") != NULL
314       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_refsys") != NULL
315       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_refsyslabel") != NULL
316       || msOWSLookupMetadata(&(lp->metadata), "CO", "bands_interval") != NULL )
317     return;
318 
319   /* OK, we have decided to fill in the information. */
320 
321   msInsertHashTable( &(lp->metadata), "wcs_bands_name", "bands" );
322   msInsertHashTable( &(lp->metadata), "wcs_bands_label", "Bands/Channels/Samples" );
323   msInsertHashTable( &(lp->metadata), "wcs_bands_rangeitem", "_bands" ); /* ? */
324 
325   bufferSize = cm->bandcount*30+30;
326   bandlist = (char *) msSmallMalloc(bufferSize);
327   strcpy( bandlist, "1" );
328   for( i = 1; i < cm->bandcount; i++ )
329     snprintf( bandlist+strlen(bandlist), bufferSize-strlen(bandlist), ",%d", i+1 );
330 
331   msInsertHashTable( &(lp->metadata), "wcs_bands_values", bandlist );
332   free( bandlist );
333 }
334 
335 /************************************************************************/
336 /*                         msWCSParseRequest()                          */
337 /************************************************************************/
338 
msWCSParseRequest(cgiRequestObj * request,wcsParamsObj * params,mapObj * map)339 static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params, mapObj *map)
340 {
341   int i, n;
342   char **tokens;
343 
344   if(!request || !params) /* nothing to do */
345     return MS_SUCCESS;
346 
347   /* -------------------------------------------------------------------- */
348   /*      Check if this appears to be an XML POST WCS request.            */
349   /* -------------------------------------------------------------------- */
350 
351   msDebug("msWCSParseRequest(): request is %s.\n", (request->type == MS_POST_REQUEST)?"POST":"KVP");
352 
353   if( request->type == MS_POST_REQUEST
354       && request->postrequest ) {
355 #if defined(USE_LIBXML2)
356     xmlDocPtr doc = NULL;
357     xmlNodePtr root = NULL, child = NULL;
358     char *tmp = NULL;
359 
360     /* parse to DOM-Structure and get root element */
361     if((doc = xmlParseMemory(request->postrequest, strlen(request->postrequest)))
362         == NULL) {
363       xmlErrorPtr error = xmlGetLastError();
364       msSetError(MS_WCSERR, "XML parsing error: %s",
365                  "msWCSParseRequest()", error->message);
366       return MS_FAILURE;
367     }
368     root = xmlDocGetRootElement(doc);
369 
370     /* Get service, version and request from root */
371     params->request = msStrdup((char *) root->name);
372     if ((tmp = (char *) xmlGetProp(root, BAD_CAST "service")) != NULL)
373       params->service = tmp;
374     if ((tmp = (char *) xmlGetProp(root, BAD_CAST "version")) != NULL)
375       params->version = tmp;
376 
377     /* search first level children, either CoverageID,  */
378     for (child = root->children; child != NULL; child = child->next) {
379       if (EQUAL((char *)child->name, "AcceptVersions")) {
380         /* will be overridden to 1.1.1 anyway */
381       } else if (EQUAL((char *) child->name, "UpdateSequence")) {
382         params->updatesequence = (char *)xmlNodeGetContent(child);
383       } else if (EQUAL((char *) child->name, "Sections")) {
384         xmlNodePtr sectionNode = NULL;
385         /* concatenate all sections by ',' */
386         for(sectionNode = child->children; sectionNode != NULL; sectionNode = sectionNode->next) {
387           char *content;
388           if(!EQUAL((char *)sectionNode->name, "Section"))
389             continue;
390           content = (char *)xmlNodeGetContent(sectionNode);
391           if(!params->section) {
392             params->section = content;
393           } else {
394             params->section = msStringConcatenate(params->section, ",");
395             params->section = msStringConcatenate(params->section, content);
396             xmlFree(content);
397           }
398         }
399       } else if(EQUAL((char *) child->name, "AcceptFormats")) {
400         /* TODO: implement */
401       } else if(EQUAL((char *) child->name, "Identifier")) {
402         char *content = (char *)xmlNodeGetContent(child);
403         params->coverages = CSLAddString(params->coverages, content);
404         xmlFree(content);
405       } else if(EQUAL((char *) child->name, "DomainSubset")) {
406         xmlNodePtr tmpNode = NULL;
407         for(tmpNode = child->children; tmpNode != NULL; tmpNode = tmpNode->next) {
408           if(EQUAL((char *) tmpNode->name, "BoundingBox")) {
409             xmlNodePtr cornerNode = NULL;
410             params->crs = (char *)xmlGetProp(tmpNode, BAD_CAST "crs");
411             if( strncasecmp(params->crs,"urn:ogc:def:crs:",16) == 0
412                 && strncasecmp(params->crs+strlen(params->crs)-8,"imageCRS",8)==0)
413               strcpy( params->crs, "imageCRS" );
414             for(cornerNode = tmpNode->children; cornerNode != NULL; cornerNode = cornerNode->next) {
415               if(EQUAL((char *) cornerNode->name, "LowerCorner")) {
416                 char *value = (char *)xmlNodeGetContent(cornerNode);
417                 tokens = msStringSplit(value, ' ', &n);
418                 if(tokens==NULL || n < 2) {
419                   msSetError(MS_WCSERR, "Wrong number of arguments for LowerCorner",
420                              "msWCSParseRequest()");
421                   return msWCSException(map, "InvalidParameterValue", "LowerCorner",
422                                         params->version );
423                 }
424                 params->bbox.minx = atof(tokens[0]);
425                 params->bbox.miny = atof(tokens[1]);
426                 msFreeCharArray(tokens, n);
427                 xmlFree(value);
428               }
429               if(EQUAL((char *) cornerNode->name, "UpperCorner")) {
430                 char *value = (char *)xmlNodeGetContent(cornerNode);
431                 tokens = msStringSplit(value, ' ', &n);
432                 if(tokens==NULL || n < 2) {
433                   msSetError(MS_WCSERR, "Wrong number of arguments for UpperCorner",
434                              "msWCSParseRequest()");
435                   return msWCSException(map, "InvalidParameterValue", "UpperCorner",
436                                         params->version );
437                 }
438                 params->bbox.maxx = atof(tokens[0]);
439                 params->bbox.maxy = atof(tokens[1]);
440                 msFreeCharArray(tokens, n);
441                 xmlFree(value);
442               }
443             }
444           }
445         }
446       } else if(EQUAL((char *) child->name, "RangeSubset")) {
447         /* TODO: not implemented in mapserver WCS 1.1? */
448       } else if(EQUAL((char *) child->name, "Output")) {
449         xmlNodePtr tmpNode = NULL;
450         params->format = (char *)xmlGetProp(child, BAD_CAST "format");
451         for(tmpNode = child->children; tmpNode != NULL; tmpNode = tmpNode->next) {
452           if(EQUAL((char *) tmpNode->name, "GridCRS")) {
453             xmlNodePtr crsNode = NULL;
454             for(crsNode = tmpNode->children; crsNode != NULL; crsNode = crsNode->next) {
455               if(EQUAL((char *) crsNode->name, "GridBaseCRS")) {
456                 params->response_crs = (char *) xmlNodeGetContent(crsNode);
457               } else if (EQUAL((char *) crsNode->name, "GridOrigin")) {
458                 char *value = (char *)xmlNodeGetContent(crsNode);
459                 tokens = msStringSplit(value, ' ', &n);
460                 if(tokens==NULL || n < 2) {
461                   msSetError(MS_WCSERR, "Wrong number of arguments for GridOrigin",
462                              "msWCSParseRequest()");
463                   return msWCSException(map, "InvalidParameterValue", "GridOffsets",
464                                         params->version );
465                 }
466                 params->originx = atof(tokens[0]);
467                 params->originy = atof(tokens[1]);
468                 msFreeCharArray(tokens, n);
469                 xmlFree(value);
470               } else if (EQUAL((char *) crsNode->name, "GridOffsets")) {
471                 char *value = (char *)xmlNodeGetContent(crsNode);
472                 tokens = msStringSplit(value, ' ', &n);
473                 if(tokens==NULL || n < 2) {
474                   msSetError(MS_WCSERR, "Wrong number of arguments for GridOffsets",
475                              "msWCSParseRequest()");
476                   return msWCSException(map, "InvalidParameterValue", "GridOffsets",
477                                         params->version );
478                 }
479                 /* take absolute values to convert to positive RESX/RESY style
480                 WCS 1.0 behavior.  *but* this does break some possibilities! */
481                 params->resx = fabs(atof(tokens[0]));
482                 params->resy = fabs(atof(tokens[1]));
483                 msFreeCharArray(tokens, n);
484                 xmlFree(value);
485               }
486             }
487           }
488         }
489       }
490     }
491     xmlFreeDoc(doc);
492     xmlCleanupParser();
493     return MS_SUCCESS;
494 #else /* defined(USE_LIBXML2) */
495     msSetError(MS_WCSERR, "To enable POST requests, MapServer has to "
496                "be compiled with libxml2.", "msWCSParseRequest()");
497     return MS_FAILURE;
498 #endif /* defined(USE_LIBXML2) */
499   }
500 
501   /* -------------------------------------------------------------------- */
502   /*      Extract WCS KVP Parameters.                                     */
503   /* -------------------------------------------------------------------- */
504   if(request->NumParams > 0) {
505     for(i=0; i<request->NumParams; i++) {
506 
507       if(strcasecmp(request->ParamNames[i], "VERSION") == 0)
508         params->version = msStrdup(request->ParamValues[i]);
509       if(strcasecmp(request->ParamNames[i], "UPDATESEQUENCE") == 0)
510         params->updatesequence = msStrdup(request->ParamValues[i]);
511       else if(strcasecmp(request->ParamNames[i], "REQUEST") == 0)
512         params->request = msStrdup(request->ParamValues[i]);
513       else if(strcasecmp(request->ParamNames[i], "INTERPOLATION") == 0)
514         params->interpolation = msStrdup(request->ParamValues[i]);
515       else if(strcasecmp(request->ParamNames[i], "SERVICE") == 0)
516         params->service = msStrdup(request->ParamValues[i]);
517       else if(strcasecmp(request->ParamNames[i], "SECTION") == 0) /* 1.0 */
518         params->section = msStrdup(request->ParamValues[i]); /* TODO: validate value here */
519       else if(strcasecmp(request->ParamNames[i], "SECTIONS") == 0) /* 1.1 */
520         params->section = msStrdup(request->ParamValues[i]); /* TODO: validate value here */
521 
522       /* GetCoverage parameters. */
523       else if(strcasecmp(request->ParamNames[i], "BBOX") == 0) {
524         tokens = msStringSplit(request->ParamValues[i], ',', &n);
525         if(tokens==NULL || n != 4) {
526           msSetError(MS_WCSERR, "Wrong number of arguments for BBOX.", "msWCSParseRequest()");
527           return msWCSException(map, "InvalidParameterValue", "bbox",
528                                 params->version );
529         }
530         params->bbox.minx = atof(tokens[0]);
531         params->bbox.miny = atof(tokens[1]);
532         params->bbox.maxx = atof(tokens[2]);
533         params->bbox.maxy = atof(tokens[3]);
534 
535         msFreeCharArray(tokens, n);
536       } else if(strcasecmp(request->ParamNames[i], "RESX") == 0)
537         params->resx = atof(request->ParamValues[i]);
538       else if(strcasecmp(request->ParamNames[i], "RESY") == 0)
539         params->resy = atof(request->ParamValues[i]);
540       else if(strcasecmp(request->ParamNames[i], "WIDTH") == 0)
541         params->width = atoi(request->ParamValues[i]);
542       else if(strcasecmp(request->ParamNames[i], "HEIGHT") == 0)
543         params->height = atoi(request->ParamValues[i]);
544       else if(strcasecmp(request->ParamNames[i], "COVERAGE") == 0)
545         params->coverages = CSLAddString(params->coverages, request->ParamValues[i]);
546       else if(strcasecmp(request->ParamNames[i], "TIME") == 0)
547         params->time = msStrdup(request->ParamValues[i]);
548       else if(strcasecmp(request->ParamNames[i], "FORMAT") == 0)
549         params->format = msStrdup(request->ParamValues[i]);
550       else if(strcasecmp(request->ParamNames[i], "CRS") == 0)
551         params->crs = msStrdup(request->ParamValues[i]);
552       else if(strcasecmp(request->ParamNames[i], "RESPONSE_CRS") == 0)
553         params->response_crs = msStrdup(request->ParamValues[i]);
554 
555       /* WCS 1.1 DescribeCoverage and GetCoverage ... */
556       else if(strcasecmp(request->ParamNames[i], "IDENTIFIER") == 0
557               || strcasecmp(request->ParamNames[i], "IDENTIFIERS") == 0 ) {
558         msDebug("msWCSParseRequest(): Whole String: %s\n", request->ParamValues[i]);
559         params->coverages = CSLAddString(params->coverages, request->ParamValues[i]);
560       }
561       /* WCS 1.1 style BOUNDINGBOX */
562       else if(strcasecmp(request->ParamNames[i], "BOUNDINGBOX") == 0) {
563         tokens = msStringSplit(request->ParamValues[i], ',', &n);
564         if(tokens==NULL || n < 5) {
565           msSetError(MS_WCSERR, "Wrong number of arguments for BOUNDINGBOX.", "msWCSParseRequest()");
566           return msWCSException(map, "InvalidParameterValue", "boundingbox",
567                                 params->version );
568         }
569 
570         /* NOTE: WCS 1.1 boundingbox is center of pixel oriented, not edge
571            like in WCS 1.0.  So bbox semantics are wonky till this is fixed
572            later in the GetCoverage processing. */
573         params->bbox.minx = atof(tokens[0]);
574         params->bbox.miny = atof(tokens[1]);
575         params->bbox.maxx = atof(tokens[2]);
576         params->bbox.maxy = atof(tokens[3]);
577 
578         params->crs = msStrdup(tokens[4]);
579         msFreeCharArray(tokens, n);
580         /* normalize imageCRS urns to simply "imageCRS" */
581         if( strncasecmp(params->crs,"urn:ogc:def:crs:",16) == 0
582             && strncasecmp(params->crs+strlen(params->crs)-8,"imageCRS",8)==0)
583           strcpy( params->crs, "imageCRS" );
584       } else if(strcasecmp(request->ParamNames[i], "GridOffsets") == 0) {
585         tokens = msStringSplit(request->ParamValues[i], ',', &n);
586         if(tokens==NULL || n < 2) {
587           msSetError(MS_WCSERR, "Wrong number of arguments for GridOffsets",
588                      "msWCSParseRequest()");
589           return msWCSException(map, "InvalidParameterValue", "GridOffsets",
590                                 params->version );
591         }
592         /* take absolute values to convert to positive RESX/RESY style
593            WCS 1.0 behavior.  *but* this does break some possibilities! */
594         params->resx = fabs(atof(tokens[0]));
595         params->resy = fabs(atof(tokens[1]));
596         msFreeCharArray(tokens, n);
597       } else if(strcasecmp(request->ParamNames[i], "GridOrigin") == 0) {
598         tokens = msStringSplit(request->ParamValues[i], ',', &n);
599         if(tokens==NULL || n < 2) {
600           msSetError(MS_WCSERR, "Wrong number of arguments for GridOrigin",
601                      "msWCSParseRequest()");
602           return msWCSException(map, "InvalidParameterValue", "GridOffsets",
603                                 params->version );
604         }
605         params->originx = atof(tokens[0]);
606         params->originy = atof(tokens[1]);
607         msFreeCharArray(tokens, n);
608       }
609 
610       /* and so on... */
611     }
612   }
613   /* we are not dealing with an XML encoded request at this point */
614   return MS_SUCCESS;
615 }
616 
617 /************************************************************************/
618 /*           msWCSGetCapabilities_Service_ResponsibleParty()            */
619 /************************************************************************/
620 
msWCSGetCapabilities_Service_ResponsibleParty(mapObj * map)621 static void msWCSGetCapabilities_Service_ResponsibleParty(mapObj *map)
622 {
623   int bEnableTelephone=MS_FALSE, bEnableAddress=MS_FALSE, bEnableOnlineResource=MS_FALSE;
624 
625   /* the WCS-specific way */
626   if(msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_individualname") ||
627       msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_organizationname")) {
628 
629     msIO_printf("<responsibleParty>\n");
630     msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_individualname", OWS_NOERR, "    <individualName>%s</individualName>\n", NULL);
631     msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_organizationname", OWS_NOERR, "    <organisationName>%s</organisationName>\n", NULL);
632     msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_positionname", OWS_NOERR, "    <positionName>%s</positionName>\n", NULL);
633 
634     if(msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_phone_voice") ||
635         msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_phone_facsimile")) bEnableTelephone = MS_TRUE;
636 
637     if(msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_deliverypoint") ||
638         msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_city") ||
639         msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_administrativearea") ||
640         msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_postalcode") ||
641         msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_country") ||
642         msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_address_electronicmailaddress")) bEnableAddress = MS_TRUE;
643 
644     if(msOWSLookupMetadata(&(map->web.metadata), "CO", "responsibleparty_onlineresource")) bEnableOnlineResource = MS_TRUE;
645 
646     if(bEnableTelephone || bEnableAddress || bEnableOnlineResource) {
647       msIO_printf("  <contactInfo>\n");
648       if(bEnableTelephone) {
649         msIO_printf("    <phone>\n");
650         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_phone_voice", OWS_NOERR, "    <voice>%s</voice>\n", NULL);
651         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_phone_facsimile", OWS_NOERR, "    <facsimile>%s</facsimile>\n", NULL);
652         msIO_printf("    </phone>\n");
653       }
654       if(bEnableAddress) {
655         msIO_printf("    <address>\n");
656         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_deliverypoint", OWS_NOERR, "    <deliveryPoint>%s</deliveryPoint>\n", NULL);
657         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_city", OWS_NOERR, "    <city>%s</city>\n", NULL);
658         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_administrativearea", OWS_NOERR, "    <administrativeArea>%s</administrativeArea>\n", NULL);
659         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_postalcode", OWS_NOERR, "    <postalCode>%s</postalCode>\n", NULL);
660         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_country", OWS_NOERR, "    <country>%s</country>\n", NULL);
661         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_address_electronicmailaddress", OWS_NOERR, "    <electronicMailAddress>%s</electronicMailAddress>\n", NULL);
662         msIO_printf("    </address>\n");
663       }
664       msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "responsibleparty_onlineresource", OWS_NOERR, "    <onlineResource xlink:type=\"simple\" xlink:href=\"%s\"/>\n", NULL);
665       msIO_printf("  </contactInfo>\n");
666     }
667 
668     msIO_printf("</responsibleParty>\n");
669 
670   } else if(msOWSLookupMetadata(&(map->web.metadata), "CO", "contactperson") ||
671             msOWSLookupMetadata(&(map->web.metadata), "CO", "contactorganization")) { /* leverage WMS contact information */
672 
673     msIO_printf("<responsibleParty>\n");
674     msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "contactperson", OWS_NOERR, "    <individualName>%s</individualName>\n", NULL);
675     msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "contactorganization", OWS_NOERR, "    <organisationName>%s</organisationName>\n", NULL);
676     msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "contactposition", OWS_NOERR, "    <positionName>%s</positionName>\n", NULL);
677 
678     if(msOWSLookupMetadata(&(map->web.metadata), "CO", "contactvoicetelephone") ||
679         msOWSLookupMetadata(&(map->web.metadata), "CO", "contactfacsimiletelephone")) bEnableTelephone = MS_TRUE;
680 
681     if(msOWSLookupMetadata(&(map->web.metadata), "CO", "address") ||
682         msOWSLookupMetadata(&(map->web.metadata), "CO", "city") ||
683         msOWSLookupMetadata(&(map->web.metadata), "CO", "stateorprovince") ||
684         msOWSLookupMetadata(&(map->web.metadata), "CO", "postcode") ||
685         msOWSLookupMetadata(&(map->web.metadata), "CO", "country") ||
686         msOWSLookupMetadata(&(map->web.metadata), "CO", "contactelectronicmailaddress")) bEnableAddress = MS_TRUE;
687 
688     if(msOWSLookupMetadata(&(map->web.metadata), "CO", "service_onlineresource")) bEnableOnlineResource = MS_TRUE;
689 
690     if(bEnableTelephone || bEnableAddress || bEnableOnlineResource) {
691       msIO_printf("  <contactInfo>\n");
692       if(bEnableTelephone) {
693         msIO_printf("    <phone>\n");
694         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "contactvoicetelephone", OWS_NOERR, "    <voice>%s</voice>\n", NULL);
695         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "contactfacsimiletelephone", OWS_NOERR, "    <facsimile>%s</facsimile>\n", NULL);
696         msIO_printf("    </phone>\n");
697       }
698       if(bEnableAddress) {
699         msIO_printf("    <address>\n");
700         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "address", OWS_NOERR, "    <deliveryPoint>%s</deliveryPoint>\n", NULL);
701         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "city", OWS_NOERR, "    <city>%s</city>\n", NULL);
702         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "stateorprovince", OWS_NOERR, "    <administrativeArea>%s</administrativeArea>\n", NULL);
703         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "postcode", OWS_NOERR, "    <postalCode>%s</postalCode>\n", NULL);
704         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "country", OWS_NOERR, "    <country>%s</country>\n", NULL);
705         msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "contactelectronicmailaddress", OWS_NOERR, "    <electronicMailAddress>%s</electronicMailAddress>\n", NULL);
706         msIO_printf("    </address>\n");
707       }
708       msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "service_onlineresource", OWS_NOERR, "    <onlineResource xlink:type=\"simple\" xlink:href=\"%s\"/>\n", NULL);
709       msIO_printf("  </contactInfo>\n");
710     }
711     msIO_printf("</responsibleParty>\n");
712   }
713 
714   return;
715 }
716 
717 /************************************************************************/
718 /*                    msWCSGetCapabilities_Service()                    */
719 /************************************************************************/
720 
msWCSGetCapabilities_Service(mapObj * map,wcsParamsObj * params)721 static int msWCSGetCapabilities_Service(mapObj *map, wcsParamsObj *params)
722 {
723   /* start the Service section, only need the full start tag if this is the only section requested */
724   if(!params->section || (params->section && strcasecmp(params->section, "/") == 0))
725     msIO_printf("<Service>\n");
726   else
727     msIO_printf("<Service\n"
728                 "   version=\"%s\" \n"
729                 "   updateSequence=\"%s\" \n"
730                 "   xmlns=\"http://www.opengis.net/wcs\" \n"
731                 "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
732                 "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
733                 "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
734                 "   xsi:schemaLocation=\"http://www.opengis.net/wcs %s/wcs/%s/wcsCapabilities.xsd\">\n", params->version, params->updatesequence, msOWSGetSchemasLocation(map), params->version);
735 
736   /* optional metadataLink */
737   msOWSPrintURLType(stdout, &(map->web.metadata), "CO", "metadatalink",
738                     OWS_NOERR,
739                     "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>",
740                     NULL, " metadataType=\"%s\"", NULL, NULL, NULL,
741                     " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE,
742                     MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL);
743 
744   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "description", OWS_NOERR, "  <description>%s</description>\n", NULL);
745   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "name", OWS_NOERR, "  <name>%s</name>\n", "MapServer WCS");
746   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "label", OWS_WARN, "  <label>%s</label>\n", NULL);
747 
748   /* we are not supporting the optional keyword type, at least not yet */
749   msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "CO", "keywordlist", "  <keywords>\n", "  </keywords>\n", "    <keyword>%s</keyword>\n", NULL);
750 
751   msWCSGetCapabilities_Service_ResponsibleParty(map);
752 
753   msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "CO", "fees", OWS_NOERR, "  <fees>%s</fees>\n", "NONE");
754   msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "CO", "accessconstraints", "  <accessConstraints>\n", "  </accessConstraints>\n", "    %s\n", "NONE");
755 
756   /* done */
757   msIO_printf("</Service>\n");
758 
759   return MS_SUCCESS;
760 }
761 
762 /************************************************************************/
763 /*                  msWCSGetCapabilities_Capability()                   */
764 /************************************************************************/
765 
msWCSGetCapabilities_Capability(mapObj * map,wcsParamsObj * params,cgiRequestObj * req)766 static int msWCSGetCapabilities_Capability(mapObj *map, wcsParamsObj *params, cgiRequestObj *req)
767 {
768   char *script_url=NULL, *script_url_encoded=NULL;
769 
770   /* we need this server's onlineresource for the request section */
771   if((script_url=msOWSGetOnlineResource(map, "CO", "onlineresource", req)) == NULL || (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) {
772     free(script_url);
773     free(script_url_encoded);
774     return msWCSException(map, NULL, NULL, params->version );
775   }
776 
777   /* start the Capabilty section, only need the full start tag if this is the only section requested */
778   if(!params->section || (params->section && strcasecmp(params->section, "/") == 0))
779     msIO_printf("<Capability>\n");
780   else
781     msIO_printf("<Capability\n"
782                 "   version=\"%s\" \n"
783                 "   updateSequence=\"%s\" \n"
784                 "   xmlns=\"http://www.opengis.net/wcs\" \n"
785                 "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
786                 "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
787                 "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
788                 "   xsi:schemaLocation=\"http://www.opengis.net/wcs %s/wcs/%s/wcsCapabilities.xsd\">\n", params->version, params->updatesequence, msOWSGetSchemasLocation(map), params->version);
789 
790   /* describe the types of requests the server can handle */
791   msIO_printf("  <Request>\n");
792 
793   msWCSPrintRequestCapability(params->version, "GetCapabilities", script_url_encoded);
794   if (msOWSRequestIsEnabled(map, NULL, "C", "DescribeCoverage", MS_FALSE))
795     msWCSPrintRequestCapability(params->version, "DescribeCoverage", script_url_encoded);
796   if (msOWSRequestIsEnabled(map, NULL, "C", "GetCoverage", MS_FALSE))
797     msWCSPrintRequestCapability(params->version, "GetCoverage", script_url_encoded);
798 
799   msIO_printf("  </Request>\n");
800 
801   /* describe the exception formats the server can produce */
802   msIO_printf("  <Exception>\n");
803   msIO_printf("    <Format>application/vnd.ogc.se_xml</Format>\n");
804   msIO_printf("  </Exception>\n");
805 
806   /* describe any vendor specific capabilities */
807   /* msIO_printf("  <VendorSpecificCapabilities />\n"); */ /* none yet */
808 
809   /* done */
810   msIO_printf("</Capability>\n");
811 
812   free(script_url);
813   free(script_url_encoded);
814 
815   return MS_SUCCESS;
816 }
817 
818 /************************************************************************/
819 /*             msWCSGetCapabilities_CoverageOfferingBrief()             */
820 /************************************************************************/
821 
msWCSGetCapabilities_CoverageOfferingBrief(layerObj * layer,wcsParamsObj * params,char * script_url_encoded)822 static int msWCSGetCapabilities_CoverageOfferingBrief(layerObj *layer, wcsParamsObj *params, char *script_url_encoded)
823 {
824   coverageMetadataObj cm;
825   int status;
826 
827   if((layer->status == MS_DELETE) || !msWCSIsLayerSupported(layer)) return MS_SUCCESS; /* not an error, this layer cannot be served via WCS */
828 
829   status = msWCSGetCoverageMetadata(layer, &cm);
830   if(status != MS_SUCCESS) return MS_FAILURE;
831 
832   /* start the CoverageOfferingBrief section */
833   msIO_printf("  <CoverageOfferingBrief>\n"); /* is this tag right? (I hate schemas without ANY examples) */
834 
835   /* optional metadataLink */
836   if (! msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_href"))
837     msMetadataSetGetMetadataURL(layer, script_url_encoded);
838 
839   msOWSPrintURLType(stdout, &(layer->metadata), "CO", "metadatalink",
840                     OWS_NOERR,
841                     "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>",
842                     NULL, " metadataType=\"%s\"", NULL, NULL, NULL,
843                     " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE,
844                     MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL);
845 
846   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "description", OWS_NOERR, "    <description>%s</description>\n", NULL);
847   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "name", OWS_NOERR, "    <name>%s</name>\n", layer->name);
848 
849   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "label", OWS_WARN, "    <label>%s</label>\n", NULL);
850 
851   /* TODO: add elevation ranges to lonLatEnvelope (optional) */
852   msIO_printf("    <lonLatEnvelope srsName=\"urn:ogc:def:crs:OGC:1.3:CRS84\">\n");
853   msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx, cm.llextent.miny); /* TODO: don't know if this is right */
854   msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx, cm.llextent.maxy);
855 
856   msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL, "      <gml:timePosition>%s</gml:timePosition>\n", NULL);
857 
858   msIO_printf("    </lonLatEnvelope>\n");
859 
860   /* we are not supporting the optional keyword type, at least not yet */
861   msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "keywordlist", "  <keywords>\n", "  </keywords>\n", "    <keyword>%s</keyword>\n", NULL);
862 
863   /* done */
864   msIO_printf("  </CoverageOfferingBrief>\n");
865 
866   msWCSFreeCoverageMetadata(&cm);
867 
868   return MS_SUCCESS;
869 }
870 
871 /************************************************************************/
872 /*                msWCSGetCapabilities_ContentMetadata()                */
873 /************************************************************************/
874 
msWCSGetCapabilities_ContentMetadata(mapObj * map,wcsParamsObj * params,owsRequestObj * ows_request,cgiRequestObj * req)875 static int msWCSGetCapabilities_ContentMetadata(mapObj *map, wcsParamsObj *params, owsRequestObj *ows_request, cgiRequestObj *req)
876 {
877   int i;
878   char *script_url_encoded=NULL;
879 
880   {
881       char* pszTmp = msOWSGetOnlineResource(map, "CO", "onlineresource", req);
882       script_url_encoded = msEncodeHTMLEntities(pszTmp);
883       msFree(pszTmp);
884   }
885 
886   /* start the ContentMetadata section, only need the full start tag if this is the only section requested */
887   /* TODO: add Xlink attributes for other sources of this information  */
888   if(!params->section || (params->section && strcasecmp(params->section, "/") == 0))
889     msIO_printf("<ContentMetadata>\n");
890   else
891     msIO_printf("<ContentMetadata\n"
892                 "   version=\"%s\" \n"
893                 "   updateSequence=\"%s\" \n"
894                 "   xmlns=\"http://www.opengis.net/wcs\" \n"
895                 "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
896                 "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
897                 "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
898                 "   xsi:schemaLocation=\"http://www.opengis.net/wcs %s/wcs/%s/wcsCapabilities.xsd\">\n", params->version, params->updatesequence, msOWSGetSchemasLocation(map), params->version);
899 
900   if(ows_request->numlayers == 0) {
901     msIO_printf("  <!-- WARNING: No WCS layers are enabled. Check wcs/ows_enable_request settings. -->\n");
902   } else {
903     for(i=0; i<map->numlayers; i++) {
904       if (!msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers, ows_request->numlayers))
905         continue;
906 
907       if(msWCSGetCapabilities_CoverageOfferingBrief((GET_LAYER(map, i)), params, script_url_encoded) != MS_SUCCESS ) {
908         msIO_printf("  <!-- WARNING: There was a problem with one of layers. See server log for details. -->\n");
909       }
910     }
911   }
912 
913   msFree(script_url_encoded);
914 
915   /* done */
916   msIO_printf("</ContentMetadata>\n");
917 
918   return MS_SUCCESS;
919 }
920 
921 /************************************************************************/
922 /*                        msWCSGetCapabilities()                        */
923 /************************************************************************/
924 
msWCSGetCapabilities(mapObj * map,wcsParamsObj * params,cgiRequestObj * req,owsRequestObj * ows_request)925 static int msWCSGetCapabilities(mapObj *map, wcsParamsObj *params, cgiRequestObj *req, owsRequestObj *ows_request)
926 {
927   char tmpString[OWS_VERSION_MAXLEN];
928   int i, tmpInt = 0;
929   int wcsSupportedVersions[] = {OWS_1_1_2, OWS_1_1_1, OWS_1_1_0, OWS_1_0_0};
930   int wcsNumSupportedVersions = 4;
931   const char *updatesequence=NULL;
932 
933   /* check version is valid */
934   tmpInt = msOWSParseVersionString(params->version);
935   if (tmpInt == OWS_VERSION_BADFORMAT) {
936     return msWCSException(map, "InvalidParameterValue",
937                           "version", "1.0.0 ");
938   }
939 
940   /* negotiate version */
941   tmpInt = msOWSNegotiateVersion(tmpInt, wcsSupportedVersions, wcsNumSupportedVersions);
942 
943   /* set result as string and carry on */
944   free(params->version);
945   params->version = msStrdup(msOWSGetVersionString(tmpInt, tmpString));
946 
947   /* -------------------------------------------------------------------- */
948   /*      1.1.x is sufficiently different we have a whole case for        */
949   /*      it.  The remainder of this function is for 1.0.0.               */
950   /* -------------------------------------------------------------------- */
951   if( strncmp(params->version,"1.1",3) == 0 )
952     return msWCSGetCapabilities11( map, params, req, ows_request);
953 
954   updatesequence = msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
955 
956   if (params->updatesequence != NULL) {
957     i = msOWSNegotiateUpdateSequence(params->updatesequence, updatesequence);
958     if (i == 0) { /* current */
959       msSetError(MS_WCSERR, "UPDATESEQUENCE parameter (%s) is equal to server (%s)", "msWCSGetCapabilities()", params->updatesequence, updatesequence);
960       return msWCSException(map, "CurrentUpdateSequence",
961                             "updatesequence", params->version );
962     }
963     if (i > 0) { /* invalid */
964       msSetError(MS_WCSERR, "UPDATESEQUENCE parameter (%s) is higher than server (%s)", "msWCSGetCapabilities()", params->updatesequence, updatesequence);
965       return msWCSException(map, "InvalidUpdateSequence",
966                             "updatesequence", params->version );
967     }
968   }
969 
970   else { /* set default updatesequence */
971     if(!updatesequence)
972       updatesequence = "0";
973     params->updatesequence = msStrdup(updatesequence);
974   }
975 
976   /* if a bum section param is passed, throw exception */
977   if (params->section &&
978       strcasecmp(params->section, "/WCS_Capabilities/Service") != 0 &&
979       strcasecmp(params->section, "/WCS_Capabilities/Capability") != 0 &&
980       strcasecmp(params->section, "/WCS_Capabilities/ContentMetadata") != 0 &&
981       strcasecmp(params->section, "/") != 0) {
982     msIO_setHeader("Content-Type","application/vnd.ogc.se_xml; charset=UTF-8");
983     msIO_sendHeaders();
984     msSetError( MS_WCSERR,
985                 "Invalid SECTION parameter \"%s\"",
986                 "msWCSGetCapabilities()", params->section);
987     return msWCSException(map, "InvalidParameterValue", "section",
988                           params->version );
989   }
990 
991   else {
992     msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
993     msIO_sendHeaders();
994 
995     /* print common capability elements  */
996     /* TODO: DocType? */
997 
998     if (!updatesequence)
999       updatesequence = "0";
1000 
1001     msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" standalone=\"no\" ?>\n");
1002 
1003     if(!params->section || (params->section && strcasecmp(params->section, "/")  == 0)) msIO_printf("<WCS_Capabilities\n"
1004           "   version=\"%s\" \n"
1005           "   updateSequence=\"%s\" \n"
1006           "   xmlns=\"http://www.opengis.net/wcs\" \n"
1007           "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
1008           "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
1009           "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1010           "   xsi:schemaLocation=\"http://www.opengis.net/wcs %s/wcs/%s/wcsCapabilities.xsd\">\n", params->version, updatesequence, msOWSGetSchemasLocation(map), params->version);
1011 
1012     /* print the various capability sections */
1013     if(!params->section || strcasecmp(params->section, "/WCS_Capabilities/Service") == 0)
1014       msWCSGetCapabilities_Service(map, params);
1015 
1016     if(!params->section || strcasecmp(params->section, "/WCS_Capabilities/Capability")  == 0)
1017       msWCSGetCapabilities_Capability(map, params, req);
1018 
1019     if(!params->section || strcasecmp(params->section, "/WCS_Capabilities/ContentMetadata")  == 0)
1020       msWCSGetCapabilities_ContentMetadata(map, params, ows_request, req);
1021 
1022     if(params->section && strcasecmp(params->section, "/")  == 0) {
1023       msWCSGetCapabilities_Service(map, params);
1024       msWCSGetCapabilities_Capability(map, params, req);
1025       msWCSGetCapabilities_ContentMetadata(map, params, ows_request, req);
1026     }
1027 
1028     /* done */
1029     if(!params->section || (params->section && strcasecmp(params->section, "/")  == 0)) msIO_printf("</WCS_Capabilities>\n");
1030   }
1031 
1032   return MS_SUCCESS;
1033 }
1034 
1035 /************************************************************************/
1036 /*               msWCSDescribeCoverage_AxisDescription()                */
1037 /************************************************************************/
1038 
msWCSDescribeCoverage_AxisDescription(layerObj * layer,char * name)1039 static int msWCSDescribeCoverage_AxisDescription(layerObj *layer, char *name)
1040 {
1041   const char *value;
1042   char tag[100]; /* should be plenty of space */
1043 
1044   msIO_printf("        <axisDescription>\n");
1045   msIO_printf("          <AxisDescription");
1046   snprintf(tag, sizeof(tag), "%s_semantic",  name); /* optional attributes follow (should escape?) */
1047   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR, " semantic=\"%s\"", NULL);
1048   snprintf(tag, sizeof(tag), "%s_refsys", name);
1049   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR, " refSys=\"%s\"", NULL);
1050   snprintf(tag, sizeof(tag), "%s_refsyslabel", name);
1051   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR, " refSysLabel=\"%s\"", NULL);
1052   msIO_printf(">\n");
1053 
1054   /* TODO: add metadataLink (optional) */
1055 
1056   snprintf(tag, sizeof(tag), "%s_description", name);
1057   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR, "            <description>%s</description>\n", NULL);
1058   /* snprintf(tag, sizeof(tag), "%s_name", name); */
1059   /* msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_WARN, "            <name>%s</name>\n", NULL); */
1060   msIO_printf("            <name>%s</name>\n", name);
1061 
1062   snprintf(tag, sizeof(tag), "%s_label", name);
1063   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_WARN, "            <label>%s</label>\n", NULL);
1064 
1065   /* Values */
1066   msIO_printf("            <values");
1067   snprintf(tag, sizeof(tag), "%s_values_semantic", name); /* optional attributes follow (should escape?) */
1068   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR, " semantic=\"%s\"", NULL);
1069   snprintf(tag, sizeof(tag), "%s_values_type", name);
1070   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", tag, OWS_NOERR, " type=\"%s\"", NULL);
1071   msIO_printf(">\n");
1072 
1073   /* single values, we do not support optional type and semantic attributes */
1074   snprintf(tag, sizeof(tag), "%s_values", name);
1075   if(msOWSLookupMetadata(&(layer->metadata), "CO", tag))
1076     msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", tag, NULL, NULL, "              <singleValue>%s</singleValue>\n", NULL);
1077 
1078   /* intervals, only one per axis for now, we do not support optional type, atomic and semantic attributes */
1079   snprintf(tag, sizeof(tag), "%s_interval", name);
1080   if((value = msOWSLookupMetadata(&(layer->metadata), "CO", tag)) != NULL) {
1081     char **tokens;
1082     int numtokens;
1083 
1084     tokens = msStringSplit(value, '/', &numtokens);
1085     if(tokens && numtokens > 0) {
1086       msIO_printf("            <interval>\n");
1087       if(numtokens >= 1) msIO_printf("            <min>%s</min>\n", tokens[0]); /* TODO: handle closure */
1088       if(numtokens >= 2) msIO_printf("            <max>%s</max>\n", tokens[1]);
1089       if(numtokens >= 3) msIO_printf("            <res>%s</res>\n", tokens[2]);
1090       msIO_printf("            </interval>\n");
1091     }
1092   }
1093 
1094   /* TODO: add default (optional) */
1095 
1096   msIO_printf("            </values>\n");
1097 
1098   msIO_printf("          </AxisDescription>\n");
1099   msIO_printf("        </axisDescription>\n");
1100 
1101   return MS_SUCCESS;
1102 }
1103 
1104 /************************************************************************/
1105 /*               msWCSDescribeCoverage_CoverageOffering()               */
1106 /************************************************************************/
1107 
msWCSDescribeCoverage_CoverageOffering(layerObj * layer,wcsParamsObj * params,char * script_url_encoded)1108 static int msWCSDescribeCoverage_CoverageOffering(layerObj *layer, wcsParamsObj *params, char *script_url_encoded)
1109 {
1110   char **tokens;
1111   int numtokens;
1112   const char *value;
1113   char *epsg_buf, *encoded_format;
1114   coverageMetadataObj cm;
1115   int i, status;
1116 
1117   if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
1118     return MS_FAILURE;
1119 
1120   if(!msWCSIsLayerSupported(layer)) return MS_SUCCESS; /* not an error, this layer cannot be served via WCS */
1121 
1122 
1123   status = msWCSGetCoverageMetadata(layer, &cm);
1124   if(status != MS_SUCCESS) return MS_FAILURE;
1125 
1126   /* fill in bands rangeset info, if required.  */
1127   msWCSSetDefaultBandsRangeSetInfo( params, &cm, layer );
1128 
1129   /* start the Coverage section */
1130   msIO_printf("  <CoverageOffering>\n");
1131 
1132   /* optional metadataLink */
1133   if (! msOWSLookupMetadata(&(layer->metadata), "CO", "metadatalink_href"))
1134     msMetadataSetGetMetadataURL(layer, script_url_encoded);
1135 
1136   msOWSPrintURLType(stdout, &(layer->metadata), "CO", "metadatalink",
1137                     OWS_NOERR,
1138                     "  <metadataLink%s%s%s%s xlink:type=\"simple\"%s/>",
1139                     NULL, " metadataType=\"%s\"", NULL, NULL, NULL,
1140                     " xlink:href=\"%s\"", MS_FALSE, MS_FALSE, MS_FALSE,
1141                     MS_FALSE, MS_TRUE, "other", NULL, NULL, NULL, NULL, NULL);
1142 
1143   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "description", OWS_NOERR, "  <description>%s</description>\n", NULL);
1144   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "name", OWS_NOERR, "  <name>%s</name>\n", layer->name);
1145 
1146   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "label", OWS_WARN, "  <label>%s</label>\n", NULL);
1147 
1148   /* TODO: add elevation ranges to lonLatEnvelope (optional) */
1149   msIO_printf("    <lonLatEnvelope srsName=\"urn:ogc:def:crs:OGC:1.3:CRS84\">\n");
1150   msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx, cm.llextent.miny);
1151   msIO_printf("      <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx, cm.llextent.maxy);
1152 
1153   msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL, "      <gml:timePosition>%s</gml:timePosition>\n", NULL);
1154 
1155   msIO_printf("    </lonLatEnvelope>\n");
1156 
1157   /* we are not supporting the optional keyword type, at least not yet */
1158   msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "keywordlist", "  <keywords>\n", "  </keywords>\n", "    <keyword>%s</keyword>\n", NULL);
1159 
1160   /* DomainSet: starting simple, just a spatial domain (gml:envelope) and optionally a temporal domain */
1161   msIO_printf("    <domainSet>\n");
1162 
1163   /* SpatialDomain */
1164   msIO_printf("      <spatialDomain>\n");
1165 
1166   /* envelope in lat/lon */
1167   msIO_printf("        <gml:Envelope srsName=\"EPSG:4326\">\n");
1168   msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.minx, cm.llextent.miny);
1169   msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.llextent.maxx, cm.llextent.maxy);
1170   msIO_printf("        </gml:Envelope>\n");
1171 
1172   /* envelope in the native srs */
1173   msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE, &epsg_buf);
1174   if(!epsg_buf) {
1175     msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata), "CO", MS_TRUE, &epsg_buf);
1176   }
1177   if(epsg_buf) {
1178     msIO_printf("        <gml:Envelope srsName=\"%s\">\n", epsg_buf);
1179     msFree(epsg_buf);
1180   } else {
1181     msIO_printf("        <!-- NativeCRSs ERROR: missing required information, no SRSs defined -->\n");
1182   }
1183   msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.extent.minx, cm.extent.miny);
1184   msIO_printf("          <gml:pos>%.15g %.15g</gml:pos>\n", cm.extent.maxx, cm.extent.maxy);
1185   msIO_printf("        </gml:Envelope>\n");
1186 
1187   /* gml:rectifiedGrid */
1188   msIO_printf("        <gml:RectifiedGrid dimension=\"2\">\n");
1189   msIO_printf("          <gml:limits>\n");
1190   msIO_printf("            <gml:GridEnvelope>\n");
1191   msIO_printf("              <gml:low>0 0</gml:low>\n");
1192   msIO_printf("              <gml:high>%d %d</gml:high>\n", cm.xsize-1, cm.ysize-1);
1193   msIO_printf("            </gml:GridEnvelope>\n");
1194   msIO_printf("          </gml:limits>\n");
1195   msIO_printf("          <gml:axisName>x</gml:axisName>\n");
1196   msIO_printf("          <gml:axisName>y</gml:axisName>\n");
1197   msIO_printf("          <gml:origin>\n");
1198   msIO_printf("            <gml:pos>%.15g %.15g</gml:pos>\n", cm.geotransform[0], cm.geotransform[3]);
1199   msIO_printf("          </gml:origin>\n");
1200   msIO_printf("          <gml:offsetVector>%.15g %.15g</gml:offsetVector>\n", cm.geotransform[1], cm.geotransform[2]); /* offset vector in X direction */
1201   msIO_printf("          <gml:offsetVector>%.15g %.15g</gml:offsetVector>\n", cm.geotransform[4], cm.geotransform[5]); /* offset vector in Y direction */
1202   msIO_printf("        </gml:RectifiedGrid>\n");
1203 
1204   msIO_printf("      </spatialDomain>\n");
1205 
1206   msWCSFreeCoverageMetadata(&cm);
1207 
1208   /* TemporalDomain */
1209 
1210   /* TODO: figure out when a temporal domain is valid, for example only tiled rasters support time as a domain, plus we need a timeitem */
1211   if(msOWSLookupMetadata(&(layer->metadata), "CO", "timeposition") || msOWSLookupMetadata(&(layer->metadata), "CO", "timeperiod")) {
1212     msIO_printf("      <temporalDomain>\n");
1213 
1214     /* TimePosition (should support a value AUTO, then we could mine positions from the timeitem) */
1215     msOWSPrintEncodeMetadataList(stdout, &(layer->metadata), "CO", "timeposition", NULL, NULL, "        <gml:timePosition>%s</gml:timePosition>\n", NULL);
1216 
1217     /* TODO:  add TimePeriod (only one per layer)  */
1218 
1219     msIO_printf("      </temporalDomain>\n");
1220   }
1221 
1222   msIO_printf("    </domainSet>\n");
1223 
1224   /* rangeSet */
1225   msIO_printf("    <rangeSet>\n");
1226   msIO_printf("      <RangeSet>\n"); /* TODO: there are some optional attributes */
1227 
1228   /* TODO: add metadataLink (optional) */
1229 
1230   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "rangeset_description", OWS_NOERR, "        <description>%s</description>\n", NULL);
1231   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "rangeset_name", OWS_WARN, "        <name>%s</name>\n", NULL);
1232 
1233   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "rangeset_label", OWS_WARN, "        <label>%s</label>\n", NULL);
1234 
1235   /* compound range sets */
1236   if((value = msOWSLookupMetadata(&(layer->metadata), "CO", "rangeset_axes")) != NULL) {
1237     tokens = msStringSplit(value, ',', &numtokens);
1238     if(tokens && numtokens > 0) {
1239       for(i=0; i<numtokens; i++)
1240         msWCSDescribeCoverage_AxisDescription(layer, tokens[i]);
1241       msFreeCharArray(tokens, numtokens);
1242     }
1243   }
1244 
1245   if((value = msOWSLookupMetadata(&(layer->metadata), "CO", "rangeset_nullvalue")) != NULL) {
1246     msIO_printf("        <nullValues>\n");
1247     msIO_printf("          <singleValue>%s</singleValue>\n", value);
1248     msIO_printf("        </nullValues>\n");
1249   }
1250 
1251   msIO_printf("      </RangeSet>\n");
1252   msIO_printf("    </rangeSet>\n");
1253 
1254   /* supportedCRSs */
1255   msIO_printf("    <supportedCRSs>\n");
1256 
1257   /* requestResposeCRSs: check the layer metadata/projection, and then the map metadata/projection if necessary (should never get to the error message) */
1258   msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_FALSE, &epsg_buf);
1259   if(!epsg_buf) {
1260     msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata), "CO", MS_FALSE, &epsg_buf);
1261   }
1262   if(epsg_buf) {
1263     tokens = msStringSplit(epsg_buf, ' ', &numtokens);
1264     if(tokens && numtokens > 0) {
1265       for(i=0; i<numtokens; i++)
1266         msIO_printf("      <requestResponseCRSs>%s</requestResponseCRSs>\n", tokens[i]);
1267       msFreeCharArray(tokens, numtokens);
1268     }
1269     msFree(epsg_buf);
1270   } else {
1271     msIO_printf("      <!-- requestResponseCRSs ERROR: missing required information, no SRSs defined -->\n");
1272   }
1273 
1274   /* nativeCRSs (only one in our case) */
1275   msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE, &epsg_buf);
1276   if(!epsg_buf) {
1277     msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata), "CO", MS_TRUE, &epsg_buf);
1278   }
1279   if(epsg_buf) {
1280     msIO_printf("      <nativeCRSs>%s</nativeCRSs>\n", epsg_buf);
1281     msFree(epsg_buf);
1282   } else {
1283     msIO_printf("      <!-- nativeCRSs ERROR: missing required information, no SRSs defined -->\n");
1284   }
1285 
1286   msIO_printf("    </supportedCRSs>\n");
1287 
1288 
1289   /* supportedFormats */
1290   msIO_printf("    <supportedFormats");
1291   msOWSPrintEncodeMetadata(stdout, &(layer->metadata), "CO", "nativeformat", OWS_NOERR, " nativeFormat=\"%s\"", NULL);
1292   msIO_printf(">\n");
1293 
1294   if( (encoded_format = msOWSGetEncodeMetadata( &(layer->metadata), "CO", "formats",
1295                                        "GTiff" )) != NULL ) {
1296     tokens = msStringSplit(encoded_format, ' ', &numtokens);
1297     if(tokens && numtokens > 0) {
1298       for(i=0; i<numtokens; i++)
1299         msIO_printf("      <formats>%s</formats>\n", tokens[i]);
1300       msFreeCharArray(tokens, numtokens);
1301     }
1302     msFree(encoded_format);
1303   }
1304   msIO_printf("    </supportedFormats>\n");
1305 
1306   msIO_printf("    <supportedInterpolations default=\"nearest neighbor\">\n");
1307   msIO_printf("      <interpolationMethod>nearest neighbor</interpolationMethod>\n" );
1308   msIO_printf("      <interpolationMethod>bilinear</interpolationMethod>\n" );
1309   /*  msIO_printf("      <interpolationMethod>bicubic</interpolationMethod>\n" ); */
1310   msIO_printf("    </supportedInterpolations>\n");
1311 
1312 
1313   /* done */
1314   msIO_printf("  </CoverageOffering>\n");
1315 
1316   return MS_SUCCESS;
1317 }
1318 
1319 /************************************************************************/
1320 /*                       msWCSDescribeCoverage()                        */
1321 /************************************************************************/
1322 
msWCSDescribeCoverage(mapObj * map,wcsParamsObj * params,owsRequestObj * ows_request,cgiRequestObj * req)1323 static int msWCSDescribeCoverage(mapObj *map, wcsParamsObj *params, owsRequestObj *ows_request, cgiRequestObj *req)
1324 {
1325   int i = 0,j = 0, k = 0;
1326   const char *updatesequence=NULL;
1327   char **coverages=NULL;
1328   int numcoverages=0;
1329 
1330   char *coverageName=NULL;
1331   char *script_url_encoded=NULL;
1332 
1333   /* -------------------------------------------------------------------- */
1334   /*      1.1.x is sufficiently different we have a whole case for        */
1335   /*      it.  The remainder of this function is for 1.0.0.               */
1336   /* -------------------------------------------------------------------- */
1337   if( strncmp(params->version,"1.1",3) == 0 )
1338     return msWCSDescribeCoverage11( map, params, ows_request);
1339 
1340   /* -------------------------------------------------------------------- */
1341   /*      Process 1.0.0...                                                */
1342   /* -------------------------------------------------------------------- */
1343 
1344   if(params->coverages) { /* use the list, but validate it first */
1345     for(j=0; params->coverages[j]; j++) {
1346       coverages = msStringSplit(params->coverages[j], ',', &numcoverages);
1347       for(k=0; k<numcoverages; k++) {
1348 
1349         for(i=0; i<map->numlayers; i++) {
1350           coverageName = msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO", "name", GET_LAYER(map, i)->name);
1351           if( coverageName != NULL && EQUAL(coverageName, coverages[k]) &&
1352               (msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers, ows_request->numlayers)) ) {
1353             msFree(coverageName);
1354             break;
1355           }
1356           msFree(coverageName);
1357         }
1358 
1359         /* i = msGetLayerIndex(map, coverages[k]); */
1360         if(i == map->numlayers) { /* coverage not found */
1361           msSetError( MS_WCSERR, "COVERAGE %s cannot be opened / does not exist. A layer might be disabled for \
1362 this request. Check wcs/ows_enable_request settings.", "msWCSDescribeCoverage()", coverages[k]);
1363           return msWCSException(map, "CoverageNotDefined", "coverage", params->version );
1364         }
1365       } /* next coverage */
1366       msFreeCharArray(coverages,numcoverages);
1367     }
1368   }
1369 
1370   updatesequence = msOWSLookupMetadata(&(map->web.metadata), "CO", "updatesequence");
1371   if (!updatesequence)
1372     updatesequence = "0";
1373 
1374   /* printf("Content-Type: application/vnd.ogc.se_xml%c%c",10,10); */
1375   msIO_setHeader("Content-Type","text/xml; charset=UTF-8");
1376   msIO_sendHeaders();
1377 
1378   /* print common capability elements  */
1379   msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
1380 
1381   /* start the DescribeCoverage section */
1382   msIO_printf("<CoverageDescription\n"
1383               "   version=\"%s\" \n"
1384               "   updateSequence=\"%s\" \n"
1385               "   xmlns=\"http://www.opengis.net/wcs\" \n"
1386               "   xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n"
1387               "   xmlns:gml=\"http://www.opengis.net/gml\" \n"
1388               "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
1389               "   xsi:schemaLocation=\"http://www.opengis.net/wcs %s/wcs/%s/describeCoverage.xsd\">\n", params->version, updatesequence, msOWSGetSchemasLocation(map), params->version);
1390 
1391   {
1392     char* pszTmp = msOWSGetOnlineResource(map, "CO", "onlineresource", req);
1393     script_url_encoded = msEncodeHTMLEntities(pszTmp);
1394     msFree(pszTmp);
1395   }
1396 
1397   if(params->coverages) { /* use the list */
1398     for( j = 0; params->coverages[j]; j++ ) {
1399       coverages = msStringSplit(params->coverages[j], ',', &numcoverages);
1400       for(k=0; k<numcoverages; k++) {
1401         for(i=0; i<map->numlayers; i++) {
1402           coverageName = msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO", "name", GET_LAYER(map, i)->name);
1403           if( coverageName != NULL && EQUAL(coverageName, coverages[k]) ) {
1404             msFree(coverageName);
1405             break;
1406           }
1407           msFree(coverageName);
1408         }
1409         msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params, script_url_encoded);
1410       }
1411       msFreeCharArray(coverages,numcoverages);
1412     }
1413   } else { /* return all layers */
1414     for(i=0; i<map->numlayers; i++) {
1415       if (!msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers, ows_request->numlayers))
1416         continue;
1417 
1418       msWCSDescribeCoverage_CoverageOffering((GET_LAYER(map, i)), params, script_url_encoded);
1419     }
1420   }
1421 
1422   msFree(script_url_encoded);
1423 
1424   /* done */
1425   msIO_printf("</CoverageDescription>\n");
1426 
1427   return MS_SUCCESS;
1428 }
1429 
1430 /************************************************************************/
1431 /*                       msWCSGetCoverageBands10()                      */
1432 /************************************************************************/
1433 
msWCSGetCoverageBands10(mapObj * map,cgiRequestObj * request,wcsParamsObj * params,layerObj * lp,char ** p_bandlist)1434 static int msWCSGetCoverageBands10( mapObj *map, cgiRequestObj *request,
1435                                     wcsParamsObj *params, layerObj *lp,
1436                                     char **p_bandlist )
1437 
1438 {
1439   const char *value = NULL;
1440   int i;
1441 
1442   /* Are there any non-spatio/temporal ranges to do subsetting on (e.g. bands) */
1443   value = msOWSLookupMetadata(&(lp->metadata), "CO", "rangeset_axes"); /* this will get all the compound range sets */
1444   if(value) {
1445     char **tokens;
1446     int numtokens;
1447     char tag[100];
1448     const char *rangeitem;
1449 
1450     tokens = msStringSplit(value, ',', &numtokens);
1451 
1452     for(i=0; i<numtokens; i++) {
1453       if((value = msWCSGetRequestParameter(request, tokens[i])) == NULL) continue; /* next rangeset parameter */
1454 
1455       /* ok, a parameter has been passed which matches a token in wcs_rangeset_axes */
1456       if(msWCSValidateRangeSetParam(lp, tokens[i], value) != MS_SUCCESS) {
1457         int ret;
1458         msSetError( MS_WCSERR, "Error specifying \"%s\" parameter value(s).", "msWCSGetCoverage()", tokens[i]);
1459         ret = msWCSException(map, "InvalidParameterValue", tokens[i], params->version );
1460         msFreeCharArray(tokens, numtokens);
1461         return ret;
1462       }
1463 
1464       /* xxxxx_rangeitem tells us how to subset */
1465       snprintf(tag, sizeof(tag), "%s_rangeitem", tokens[i]);
1466       if((rangeitem = msOWSLookupMetadata(&(lp->metadata), "CO", tag)) == NULL) {
1467         msSetError( MS_WCSERR, "Missing required metadata element \"%s\", unable to process %s=%s.", "msWCSGetCoverage()", tag, tokens[i], value);
1468         msFreeCharArray(tokens, numtokens);
1469         return msWCSException(map, NULL, NULL, params->version);
1470       }
1471 
1472       if(strcasecmp(rangeitem, "_bands") == 0) { /* special case, subset bands */
1473         *p_bandlist = msWCSConvertRangeSetToString(value);
1474 
1475         if(!*p_bandlist) {
1476           msSetError( MS_WCSERR, "Error specifying \"%s\" parameter value(s).", "msWCSGetCoverage()", tokens[i]);
1477           msFreeCharArray(tokens, numtokens);
1478           return msWCSException(map, NULL, NULL, params->version );
1479         }
1480       } else if(strcasecmp(rangeitem, "_pixels") == 0) { /* special case, subset pixels */
1481         msFreeCharArray(tokens, numtokens);
1482         msSetError( MS_WCSERR, "Arbitrary range sets based on pixel values are not yet supported.", "msWCSGetCoverage()" );
1483         return msWCSException(map, NULL, NULL, params->version);
1484       } else {
1485         msFreeCharArray(tokens, numtokens);
1486         msSetError( MS_WCSERR, "Arbitrary range sets based on tile (i.e. image) attributes are not yet supported.", "msWCSGetCoverage()" );
1487         return msWCSException(map, NULL, NULL, params->version );
1488       }
1489     }
1490     /* clean-up */
1491     msFreeCharArray(tokens, numtokens);
1492   }
1493 
1494   return MS_SUCCESS;
1495 }
1496 
1497 /************************************************************************/
1498 /*                   msWCSGetCoverage_ImageCRSSetup()                   */
1499 /*                                                                      */
1500 /*      The request was in imageCRS - update the map projection to      */
1501 /*      map the native projection of the layer, and reset the           */
1502 /*      bounding box to match the projected bounds corresponding to     */
1503 /*      the imageCRS request.                                           */
1504 /************************************************************************/
1505 
msWCSGetCoverage_ImageCRSSetup(mapObj * map,cgiRequestObj * request,wcsParamsObj * params,coverageMetadataObj * cm,layerObj * layer)1506 static int msWCSGetCoverage_ImageCRSSetup(
1507   mapObj *map, cgiRequestObj *request, wcsParamsObj *params,
1508   coverageMetadataObj *cm, layerObj *layer )
1509 
1510 {
1511   /* -------------------------------------------------------------------- */
1512   /*      Load map with the layer (coverage) coordinate system.  We       */
1513   /*      really need a set projectionObj from projectionObj function!    */
1514   /* -------------------------------------------------------------------- */
1515   char *layer_proj = msGetProjectionString( &(layer->projection) );
1516 
1517   if (msLoadProjectionString(&(map->projection), layer_proj) != 0) {
1518     msFree(layer_proj);
1519     return msWCSException( map, NULL, NULL, params->version );
1520   }
1521 
1522   free( layer_proj );
1523   layer_proj = NULL;
1524 
1525   /* -------------------------------------------------------------------- */
1526   /*      Reset bounding box.                                             */
1527   /* -------------------------------------------------------------------- */
1528   if( params->bbox.maxx != params->bbox.minx ) {
1529     rectObj orig_bbox = params->bbox;
1530 
1531     params->bbox.minx =
1532       cm->geotransform[0]
1533       + orig_bbox.minx * cm->geotransform[1]
1534       + orig_bbox.miny * cm->geotransform[2];
1535     params->bbox.maxy =
1536       cm->geotransform[3]
1537       + orig_bbox.minx * cm->geotransform[4]
1538       + orig_bbox.miny * cm->geotransform[5];
1539     params->bbox.maxx =
1540       cm->geotransform[0]
1541       + (orig_bbox.maxx+1) * cm->geotransform[1]
1542       + (orig_bbox.maxy+1) * cm->geotransform[2];
1543     params->bbox.miny =
1544       cm->geotransform[3]
1545       + (orig_bbox.maxx+1) * cm->geotransform[4]
1546       + (orig_bbox.maxy+1) * cm->geotransform[5];
1547 
1548     /* WCS 1.1 boundbox is center of pixel oriented. */
1549     if( strncasecmp(params->version,"1.1",3) == 0 ) {
1550       params->bbox.minx += cm->geotransform[1]/2 + cm->geotransform[2]/2;
1551       params->bbox.maxx -= cm->geotransform[1]/2 + cm->geotransform[2]/2;
1552       params->bbox.maxy += cm->geotransform[4]/2 + cm->geotransform[5]/2;
1553       params->bbox.miny -= cm->geotransform[4]/2 + cm->geotransform[5]/2;
1554     }
1555   }
1556 
1557   /* -------------------------------------------------------------------- */
1558   /*      Reset resolution.                                               */
1559   /* -------------------------------------------------------------------- */
1560   if( params->resx != 0.0 ) {
1561     params->resx = cm->geotransform[1] * params->resx;
1562     params->resy = fabs(cm->geotransform[5] * params->resy);
1563   }
1564 
1565   return MS_SUCCESS;
1566 }
1567 
1568 /************************************************************************/
1569 /*                    msWCSApplyLayerCreationOptions()                  */
1570 /************************************************************************/
1571 
msWCSApplyLayerCreationOptions(layerObj * lp,outputFormatObj * format,const char * bandlist)1572 void msWCSApplyLayerCreationOptions(layerObj* lp,
1573                                     outputFormatObj* format,
1574                                     const char* bandlist)
1575 
1576 {
1577     const char* pszKey;
1578     char szKeyBeginning[256];
1579     size_t nKeyBeginningLength;
1580     int nBands = 0;
1581     char** papszBandNumbers = msStringSplit(bandlist, ' ', &nBands);
1582 
1583     snprintf(szKeyBeginning, sizeof(szKeyBeginning),
1584              "wcs_outputformat_%s_creationoption_", format->name);
1585     nKeyBeginningLength = strlen(szKeyBeginning);
1586 
1587     pszKey = msFirstKeyFromHashTable( &(lp->metadata) );
1588     for( ; pszKey != NULL;
1589            pszKey = msNextKeyFromHashTable( &(lp->metadata), pszKey) )
1590     {
1591         if( strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0 )
1592         {
1593             const char* pszValue = msLookupHashTable( &(lp->metadata), pszKey);
1594             const char* pszGDALKey = pszKey + nKeyBeginningLength;
1595             if( EQUALN(pszGDALKey, "BAND_", strlen("BAND_")) )
1596             {
1597                 /* Remap BAND specific creation option to the real output
1598                  * band number, given the band subset of the request */
1599                 int nKeyOriBandNumber = atoi(pszGDALKey + strlen("BAND_"));
1600                 int nTargetBandNumber = -1;
1601                 int i;
1602                 for(i = 0; i < nBands; i++ )
1603                 {
1604                     if( nKeyOriBandNumber == atoi(papszBandNumbers[i]) )
1605                     {
1606                         nTargetBandNumber = i + 1;
1607                         break;
1608                     }
1609                 }
1610                 if( nTargetBandNumber > 0 )
1611                 {
1612                     char szModKey[256];
1613                     const char* pszAfterBand =
1614                         strchr(pszGDALKey + strlen("BAND_"), '_');
1615                     if( pszAfterBand != NULL )
1616                     {
1617                         snprintf(szModKey, sizeof(szModKey),
1618                                  "BAND_%d%s",
1619                                  nTargetBandNumber,
1620                                  pszAfterBand);
1621                         if( lp->debug >= MS_DEBUGLEVEL_VVV ) {
1622                             msDebug("Setting GDAL %s=%s creation option\n",
1623                                     szModKey, pszValue);
1624                         }
1625                         msSetOutputFormatOption(format, szModKey, pszValue);
1626                     }
1627                 }
1628             }
1629             else
1630             {
1631                 if( lp->debug >= MS_DEBUGLEVEL_VVV ) {
1632                     msDebug("Setting GDAL %s=%s creation option\n",
1633                             pszGDALKey, pszValue);
1634                 }
1635                 msSetOutputFormatOption(format, pszGDALKey, pszValue);
1636             }
1637         }
1638     }
1639 
1640     msFreeCharArray( papszBandNumbers, nBands );
1641 }
1642 
1643 /************************************************************************/
1644 /*               msWCSApplyDatasetMetadataAsCreationOptions()           */
1645 /************************************************************************/
1646 
msWCSApplyDatasetMetadataAsCreationOptions(layerObj * lp,outputFormatObj * format,const char * bandlist,void * hDSIn)1647 void msWCSApplyDatasetMetadataAsCreationOptions(layerObj* lp,
1648                                                 outputFormatObj* format,
1649                                                 const char* bandlist,
1650                                                 void* hDSIn)
1651 {
1652     /* Requires GDAL 2.3 in practice. */
1653     /* Automatic forwarding of input dataset metadata if it is GRIB and the */
1654     /* output is GRIB as well, and wcs_outputformat_GRIB_creationoption* are */
1655     /* not defined. */
1656     GDALDatasetH hDS = (GDALDatasetH)hDSIn;
1657     if( hDS && GDALGetDatasetDriver(hDS) &&
1658         EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hDS)), "GRIB") &&
1659         EQUAL(format->driver, "GDAL/GRIB") )
1660     {
1661         const char* pszKey;
1662         char szKeyBeginning[256];
1663         size_t nKeyBeginningLength;
1664         int bWCSMetadataFound = MS_FALSE;
1665 
1666         snprintf(szKeyBeginning, sizeof(szKeyBeginning),
1667                 "wcs_outputformat_%s_creationoption_", format->name);
1668         nKeyBeginningLength = strlen(szKeyBeginning);
1669 
1670         for( pszKey = msFirstKeyFromHashTable( &(lp->metadata) );
1671              pszKey != NULL;
1672              pszKey = msNextKeyFromHashTable( &(lp->metadata), pszKey) )
1673         {
1674             if( strncmp(pszKey, szKeyBeginning, nKeyBeginningLength) == 0 )
1675             {
1676                 bWCSMetadataFound = MS_TRUE;
1677                 break;
1678             }
1679         }
1680         if( !bWCSMetadataFound )
1681         {
1682             int nBands = 0;
1683             char** papszBandNumbers = msStringSplit(bandlist, ' ', &nBands);
1684             int i;
1685             for(i = 0; i < nBands; i++ )
1686             {
1687                 int nSrcBand = atoi(papszBandNumbers[i]);
1688                 int nDstBand = i + 1;
1689                 GDALRasterBandH hBand = GDALGetRasterBand(hDS, nSrcBand);
1690                 if( hBand )
1691                 {
1692                     char** papszMD = GDALGetMetadata(hBand, NULL);
1693                     const char* pszMDI = CSLFetchNameValue(papszMD, "GRIB_IDS");
1694                     // Make sure it is a GRIB2 band
1695                     if( pszMDI )
1696                     {
1697                         char szKey[256];
1698                         sprintf(szKey, "BAND_%d_IDS", nDstBand);
1699                         msSetOutputFormatOption(format, szKey, pszMDI);
1700 
1701                         sprintf(szKey, "BAND_%d_DISCIPLINE", nDstBand);
1702                         msSetOutputFormatOption(format, szKey,
1703                                 CSLFetchNameValue(papszMD, "DISCIPLINE"));
1704 
1705                         sprintf(szKey, "BAND_%d_PDS_PDTN", nDstBand);
1706                         msSetOutputFormatOption(format, szKey,
1707                                 CSLFetchNameValue(papszMD, "GRIB_PDS_PDTN"));
1708 
1709                         sprintf(szKey, "BAND_%d_PDS_TEMPLATE_NUMBERS",
1710                                 nDstBand);
1711                         msSetOutputFormatOption(format, szKey,
1712                                 CSLFetchNameValue(papszMD,
1713                                                   "GRIB_PDS_TEMPLATE_NUMBERS"));
1714                     }
1715                 }
1716             }
1717             msFreeCharArray( papszBandNumbers, nBands );
1718         }
1719     }
1720 }
1721 
1722 /************************************************************************/
1723 /*                          msWCSGetCoverage()                          */
1724 /************************************************************************/
1725 
msWCSGetCoverage(mapObj * map,cgiRequestObj * request,wcsParamsObj * params,owsRequestObj * ows_request)1726 static int msWCSGetCoverage(mapObj *map, cgiRequestObj *request,
1727                             wcsParamsObj *params, owsRequestObj *ows_request)
1728 {
1729   imageObj   *image;
1730   layerObj   *lp;
1731   int         status, i;
1732   const char *value;
1733   outputFormatObj *format;
1734   char *bandlist=NULL;
1735   size_t bufferSize = 0;
1736   char numbands[12]; /* should be large enough to hold the number of bands in the bandlist */
1737   coverageMetadataObj cm;
1738   rectObj reqextent;
1739   rectObj covextent;
1740   rasterBufferObj rb;
1741   int doDrawRasterLayerDraw = MS_TRUE;
1742   GDALDatasetH hDS = NULL;
1743 
1744   char *coverageName;
1745 
1746   /* make sure all required parameters are available (at least the easy ones) */
1747   if(!params->crs) {
1748     msSetError( MS_WCSERR, "Required parameter CRS was not supplied.", "msWCSGetCoverage()");
1749     return msWCSException(map, "MissingParameterValue", "crs", params->version);
1750   }
1751 
1752   if(!params->time && !params->bbox.minx && !params->bbox.miny
1753       && !params->bbox.maxx && !params->bbox.maxy) {
1754     msSetError(MS_WCSERR, "One of BBOX or TIME is required", "msWCSGetCoverage()");
1755     return msWCSException(map, "MissingParameterValue", "bbox/time", params->version);
1756   }
1757 
1758   if( params->coverages == NULL || params->coverages[0] == NULL ) {
1759     msSetError( MS_WCSERR,
1760                 "Required parameter COVERAGE was not supplied.",
1761                 "msWCSGetCoverage()");
1762     return msWCSException(map, "MissingParameterValue", "coverage", params->version);
1763   }
1764 
1765   /* For WCS 1.1, we need to normalize the axis order of the BBOX and
1766      resolution values some coordinate systems (eg. EPSG geographic) */
1767   if( strncasecmp(params->version,"1.0",3) != 0
1768       && params->crs != NULL
1769       && strncasecmp(params->crs,"urn:",4) == 0 ) {
1770     projectionObj proj;
1771 
1772     msInitProjection( &proj );
1773     msProjectionInheritContextFrom(&proj, &(map->projection));
1774     if( msLoadProjectionString( &proj, (char *) params->crs ) == 0 ) {
1775       msAxisNormalizePoints( &proj, 1,
1776                              &(params->bbox.minx),
1777                              &(params->bbox.miny) );
1778       msAxisNormalizePoints( &proj, 1,
1779                              &(params->bbox.maxx),
1780                              &(params->bbox.maxy) );
1781       msAxisNormalizePoints( &proj, 1,
1782                              &(params->resx),
1783                              &(params->resy) );
1784       msAxisNormalizePoints( &proj, 1,
1785                              &(params->originx),
1786                              &(params->originy) );
1787     } else
1788       msResetErrorList();
1789     msFreeProjection( &proj );
1790   }
1791 
1792   /* find the layer we are working with */
1793   lp = NULL;
1794   for(i=0; i<map->numlayers; i++) {
1795     coverageName = msOWSGetEncodeMetadata(&(GET_LAYER(map, i)->metadata), "CO", "name", GET_LAYER(map, i)->name);
1796     if( coverageName != NULL && EQUAL(coverageName, params->coverages[0]) &&
1797         (msIntegerInArray(GET_LAYER(map, i)->index, ows_request->enabled_layers, ows_request->numlayers)) ) {
1798       lp = GET_LAYER(map, i);
1799       free( coverageName );
1800       break;
1801     }
1802     free( coverageName );
1803   }
1804 
1805   if(lp == NULL) {
1806     msSetError( MS_WCSERR, "COVERAGE=%s not found, not in supported layer list. A layer might be disabled for \
1807 this request. Check wcs/ows_enable_request settings.", "msWCSGetCoverage()", params->coverages[0] );
1808     return msWCSException(map, "InvalidParameterValue", "coverage", params->version);
1809   }
1810 
1811   /* make sure the layer is on */
1812   lp->status = MS_ON;
1813 
1814   /* If the layer has no projection set, set it to the map's projection (#4079) */
1815   if(lp->projection.numargs <=0) {
1816     char *map_original_srs = msGetProjectionString(&(map->projection));
1817     if (msLoadProjectionString(&(lp->projection), map_original_srs) != 0) {
1818       msSetError( MS_WCSERR, "Error when setting map projection to a layer with no projection", "msWCSGetCoverage()" );
1819       free(map_original_srs);
1820       return msWCSException(map, NULL, NULL, params->version);
1821     }
1822     free(map_original_srs);
1823   }
1824 
1825   /* we need the coverage metadata, since things like numbands may not be available otherwise */
1826   status = msWCSGetCoverageMetadata(lp, &cm);
1827   if(status != MS_SUCCESS) return MS_FAILURE;
1828 
1829   /* fill in bands rangeset info, if required.  */
1830   msWCSSetDefaultBandsRangeSetInfo(params, &cm, lp);
1831 
1832   /* handle the response CRS, that is, set the map object projection */
1833   if(params->response_crs || params->crs ) {
1834     int iUnits;
1835     const char *crs_to_use = params->response_crs;
1836 
1837     if( crs_to_use == NULL )
1838       crs_to_use = params->crs;
1839 
1840     if (strncasecmp(crs_to_use, "EPSG:", 5) == 0 || strncasecmp(crs_to_use,"urn:ogc:def:crs:",16) == 0 ) {
1841       if (msLoadProjectionString(&(map->projection), (char *) crs_to_use) != 0)
1842         return msWCSException( map, NULL, NULL,params->version);
1843     } else if( strcasecmp(crs_to_use,"imageCRS") == 0 ) {
1844       /* use layer native CRS, and rework bounding box accordingly */
1845       if( msWCSGetCoverage_ImageCRSSetup( map, request, params, &cm, lp ) != MS_SUCCESS ) {
1846         msWCSFreeCoverageMetadata(&cm);
1847         return MS_FAILURE;
1848       }
1849     } else {  /* should we support WMS style AUTO: projections? (not for now) */
1850       msSetError(MS_WCSERR, "Unsupported SRS namespace (only EPSG currently supported).", "msWCSGetCoverage()");
1851       return msWCSException(map, "InvalidParameterValue", "srs", params->version);
1852     }
1853 
1854     iUnits = GetMapserverUnitUsingProj(&(map->projection));
1855     if (iUnits != -1)
1856       map->units = iUnits;
1857   }
1858 
1859   /* did we get a TIME value (support only a single value for now) */
1860   if(params->time) {
1861     int tli;
1862     layerObj *tlp=NULL;
1863 
1864     /* need to handle NOW case */
1865 
1866     /* check format of TIME parameter */
1867     if(strchr(params->time, ',')) {
1868       msWCSFreeCoverageMetadata(&cm);
1869       msSetError( MS_WCSERR, "Temporal lists are not supported, only individual values.", "msWCSGetCoverage()" );
1870       return msWCSException(map, "InvalidParameterValue", "time", params->version);
1871     }
1872     if(strchr(params->time, '/')) {
1873       msWCSFreeCoverageMetadata(&cm);
1874       msSetError( MS_WCSERR, "Temporal ranges are not supported, only individual values.", "msWCSGetCoverage()" );
1875       return msWCSException(map, "InvalidParameterValue", "time", params->version);
1876     }
1877 
1878     /* TODO: will need to expand this check if a time period is supported */
1879     value = msOWSLookupMetadata(&(lp->metadata), "CO", "timeposition");
1880     if(!value) {
1881       msWCSFreeCoverageMetadata(&cm);
1882       msSetError( MS_WCSERR, "The coverage does not support temporal subsetting.", "msWCSGetCoverage()" );
1883       return msWCSException(map, "InvalidParameterValue", "time", params->version );
1884     }
1885 
1886     /* check if timestamp is covered by the wcs_timeposition definition */
1887     if (msValidateTimeValue(params->time, value) == MS_FALSE) {
1888       msWCSFreeCoverageMetadata(&cm);
1889       msSetError( MS_WCSERR, "The coverage does not have a time position of %s.", "msWCSGetCoverage()", params->time );
1890       return msWCSException(map, "InvalidParameterValue", "time", params->version);
1891     }
1892 
1893     /* make sure layer is tiled appropriately */
1894     if(!lp->tileindex) {
1895       msWCSFreeCoverageMetadata(&cm);
1896       msSetError( MS_WCSERR, "Underlying layer is not tiled, unable to do temporal subsetting.", "msWCSGetCoverage()" );
1897       return msWCSException(map, NULL, NULL, params->version);
1898     }
1899     tli = msGetLayerIndex(map, lp->tileindex);
1900     if(tli == -1) {
1901       msWCSFreeCoverageMetadata(&cm);
1902       msSetError( MS_WCSERR, "Underlying layer does not use appropriate tiling mechanism.", "msWCSGetCoverage()" );
1903       return msWCSException(map, NULL, NULL, params->version);
1904     }
1905 
1906     tlp = (GET_LAYER(map, tli));
1907 
1908     /* make sure there is enough information to filter */
1909     value = msOWSLookupMetadata(&(lp->metadata), "CO", "timeitem");
1910     if(!tlp->filteritem && !value) {
1911       msWCSFreeCoverageMetadata(&cm);
1912       msSetError( MS_WCSERR, "Not enough information available to filter.", "msWCSGetCoverage()" );
1913       return msWCSException(map, NULL, NULL, params->version);
1914     }
1915 
1916     /* override filteritem if specified in metadata */
1917     if(value) {
1918       if(tlp->filteritem) free(tlp->filteritem);
1919       tlp->filteritem = msStrdup(value);
1920     }
1921 
1922     /* finally set the filter */
1923     msFreeExpression(&tlp->filter);
1924     msLayerSetTimeFilter(tlp, params->time, value);
1925   }
1926 
1927   if( strncasecmp(params->version,"1.0",3) == 0 )
1928     status = msWCSGetCoverageBands10( map, request, params, lp, &bandlist );
1929   else
1930     status = msWCSGetCoverageBands11( map, request, params, lp, &bandlist );
1931   if( status != MS_SUCCESS ) {
1932     msWCSFreeCoverageMetadata(&cm);
1933     return status;
1934   }
1935 
1936   /* did we get BBOX values? if not use the exent stored in the coverageMetadataObj */
1937   if( fabs((params->bbox.maxx - params->bbox.minx)) < 0.000000000001  || fabs(params->bbox.maxy - params->bbox.miny) < 0.000000000001 ) {
1938     params->bbox = cm.extent;
1939 
1940     /* WCS 1.1 boundbox is center of pixel oriented. */
1941     if( strncasecmp(params->version,"1.1",3) == 0 ) {
1942       params->bbox.minx += cm.geotransform[1]/2 + cm.geotransform[2]/2;
1943       params->bbox.maxx -= cm.geotransform[1]/2 + cm.geotransform[2]/2;
1944       params->bbox.maxy += cm.geotransform[4]/2 + cm.geotransform[5]/2;
1945       params->bbox.miny -= cm.geotransform[4]/2 + cm.geotransform[5]/2;
1946     }
1947   }
1948 
1949   /* WCS 1.1+ GridOrigin is effectively resetting the minx/maxy
1950      BOUNDINGBOX values, so apply that here */
1951   if( params->originx != 0.0 || params->originy != 0.0 ) {
1952     assert( strncasecmp(params->version,"1.0",3) != 0 ); /* should always be 1.0 in this logic. */
1953     params->bbox.minx = params->originx;
1954     params->bbox.maxy = params->originy;
1955   }
1956 
1957   /* if necessary, project the BBOX to the map->projection */
1958   if(params->response_crs && params->crs) {
1959     projectionObj tmp_proj;
1960 
1961     msInitProjection(&tmp_proj);
1962     msProjectionInheritContextFrom(&tmp_proj, &(map->projection));
1963     if (msLoadProjectionString(&tmp_proj, (char *) params->crs) != 0) {
1964       msWCSFreeCoverageMetadata(&cm);
1965       return msWCSException( map, NULL, NULL, params->version);
1966     }
1967     msProjectRect(&tmp_proj, &map->projection, &(params->bbox));
1968     msFreeProjection(&tmp_proj);
1969   }
1970 
1971   /* in WCS 1.1 the default is full resolution */
1972   if( strncasecmp(params->version,"1.1",3) == 0 && params->resx == 0.0 && params->resy == 0.0 ) {
1973     params->resx = cm.geotransform[1];
1974     params->resy = fabs(cm.geotransform[5]);
1975   }
1976 
1977   /* compute width/height from BBOX and cellsize.  */
1978   if( (params->resx == 0.0 || params->resy == 0.0) && params->width != 0 && params->height != 0 ) {
1979     assert( strncasecmp(params->version,"1.0",3) == 0 ); /* should always be 1.0 in this logic. */
1980     params->resx = (params->bbox.maxx -params->bbox.minx) / params->width;
1981     params->resy = (params->bbox.maxy -params->bbox.miny) / params->height;
1982   }
1983 
1984   /* compute cellsize/res from bbox and raster size. */
1985   if( (params->width == 0 || params->height == 0) && params->resx != 0 && params->resy != 0 ) {
1986 
1987     /* WCS 1.0 boundbox is edge of pixel oriented. */
1988     if( strncasecmp(params->version,"1.0",3) == 0 ) {
1989       params->width = (int) ((params->bbox.maxx - params->bbox.minx) / params->resx + 0.5);
1990       params->height = (int) ((params->bbox.maxy - params->bbox.miny) / params->resy + 0.5);
1991     } else {
1992       params->width = (int) ((params->bbox.maxx - params->bbox.minx) / params->resx + 1.000001);
1993       params->height = (int) ((params->bbox.maxy - params->bbox.miny) / params->resy + 1.000001);
1994 
1995       /* recompute bounding box so we get exactly the origin and
1996          resolution requested. */
1997       params->bbox.maxx = params->bbox.minx + (params->width-1) * params->resx;
1998       params->bbox.miny = params->bbox.maxy - (params->height-1) * params->resy;
1999     }
2000   }
2001 
2002   /* are we still underspecified?  */
2003   if( (params->width == 0 || params->height == 0) && (params->resx == 0.0 || params->resy == 0.0 )) {
2004     msWCSFreeCoverageMetadata(&cm);
2005     msSetError( MS_WCSERR, "A non-zero RESX/RESY or WIDTH/HEIGHT is required but neither was provided.", "msWCSGetCoverage()" );
2006     return msWCSException(map, "MissingParameterValue", "width/height/resx/resy", params->version);
2007   }
2008 
2009   map->cellsize = params->resx;
2010 
2011   /* Do we need to force special handling?  */
2012   if( fabs(params->resx/params->resy - 1.0) > 0.001 ) {
2013     map->gt.need_geotransform = MS_TRUE;
2014     if( map->debug ) msDebug( "RESX and RESY don't match.  Using geotransform/resample.\n");
2015   }
2016 
2017   /* Do we have a specified interpolation method */
2018   if( params->interpolation != NULL ) {
2019     if( strncasecmp(params->interpolation,"NEAREST",7) == 0 )
2020       msLayerSetProcessingKey(lp, "RESAMPLE", "NEAREST");
2021     else if( strcasecmp(params->interpolation,"BILINEAR") == 0 )
2022       msLayerSetProcessingKey(lp, "RESAMPLE", "BILINEAR");
2023     else if( strcasecmp(params->interpolation,"AVERAGE") == 0 )
2024       msLayerSetProcessingKey(lp, "RESAMPLE", "AVERAGE");
2025     else {
2026       msWCSFreeCoverageMetadata(&cm);
2027       msSetError( MS_WCSERR, "INTERPOLATION=%s specifies an unsupported interpolation method.", "msWCSGetCoverage()", params->interpolation );
2028       return msWCSException(map, "InvalidParameterValue", "interpolation", params->version);
2029     }
2030   }
2031 
2032   /* apply region and size to map object.  */
2033   map->width = params->width;
2034   map->height = params->height;
2035 
2036   /* Are we exceeding the MAXSIZE limit on result size? */
2037   if(map->width > map->maxsize || map->height > map->maxsize ) {
2038     msWCSFreeCoverageMetadata(&cm);
2039     msSetError(MS_WCSERR, "Raster size out of range, width and height of resulting coverage must be no more than MAXSIZE=%d.", "msWCSGetCoverage()", map->maxsize);
2040 
2041     return msWCSException(map, "InvalidParameterValue",
2042                           "width/height", params->version);
2043   }
2044 
2045   /* adjust OWS BBOX to MapServer's pixel model */
2046   if( strncasecmp(params->version,"1.0",3) == 0 ) {
2047     params->bbox.minx += params->resx*0.5;
2048     params->bbox.miny += params->resy*0.5;
2049     params->bbox.maxx -= params->resx*0.5;
2050     params->bbox.maxy -= params->resy*0.5;
2051   }
2052 
2053   map->extent = params->bbox;
2054 
2055   map->cellsize = params->resx; /* pick one, MapServer only supports square cells (what about msAdjustExtent here!) */
2056 
2057   msMapComputeGeotransform(map);
2058 
2059   /* Do we need to fake out stuff for rotated support? */
2060   if( map->gt.need_geotransform )
2061     msMapSetFakedExtent( map );
2062 
2063   map->projection.gt = map->gt;
2064 
2065   /* check for overlap */
2066 
2067   /* get extent of bbox passed, and reproject */
2068   reqextent.minx = map->extent.minx;
2069   reqextent.miny = map->extent.miny;
2070   reqextent.maxx = map->extent.maxx;
2071   reqextent.maxy = map->extent.maxy;
2072 
2073   /* reproject incoming bbox */
2074   msProjectRect(&map->projection, &lp->projection, &(reqextent));
2075 
2076   /* get extent of layer */
2077   covextent.minx = cm.extent.minx;
2078   covextent.miny = cm.extent.miny;
2079   covextent.maxx = cm.extent.maxx;
2080   covextent.maxy = cm.extent.maxy;
2081 
2082   if(msRectOverlap(&reqextent, &covextent) == MS_FALSE) {
2083     msWCSFreeCoverageMetadata(&cm);
2084     msSetError(MS_WCSERR, "Requested BBOX (%.15g,%.15g,%.15g,%.15g) is outside requested coverage BBOX (%.15g,%.15g,%.15g,%.15g)",
2085                "msWCSGetCoverage()",
2086                reqextent.minx, reqextent.miny, reqextent.maxx, reqextent.maxy,
2087                covextent.minx, covextent.miny, covextent.maxx, covextent.maxy);
2088     return msWCSException(map, "NoApplicableCode", "bbox", params->version);
2089   }
2090 
2091   /* check and make sure there is a format, and that it's valid (TODO: make sure in the layer metadata) */
2092   if(!params->format) {
2093     msWCSFreeCoverageMetadata(&cm);
2094     msSetError( MS_WCSERR,  "Missing required FORMAT parameter.", "msWCSGetCoverage()" );
2095     return msWCSException(map, "MissingParameterValue", "format", params->version);
2096   }
2097   msApplyDefaultOutputFormats(map);
2098   if(msGetOutputFormatIndex(map,params->format) == -1) {
2099     msWCSFreeCoverageMetadata(&cm);
2100     msSetError( MS_WCSERR,  "Unrecognized value for the FORMAT parameter.", "msWCSGetCoverage()" );
2101     return msWCSException(map, "InvalidParameterValue", "format",
2102                           params->version );
2103   }
2104 
2105   /* create a temporary outputformat (we likely will need to tweak parts) */
2106   format = msCloneOutputFormat(msSelectOutputFormat(map,params->format));
2107   msApplyOutputFormat(&(map->outputformat), format, MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE);
2108 
2109   if(!bandlist) { /* build a bandlist (default is ALL bands) */
2110     bufferSize = cm.bandcount*30+30;
2111     bandlist = (char *) msSmallMalloc(bufferSize);
2112     strcpy(bandlist, "1");
2113     for(i = 1; i < cm.bandcount; i++)
2114       snprintf(bandlist+strlen(bandlist), bufferSize-strlen(bandlist), ",%d", i+1);
2115   }
2116 
2117   /* apply nullvalue to the output format object if we have it */
2118   if((value = msOWSLookupMetadata(&(lp->metadata), "CO", "rangeset_nullvalue")) != NULL) {
2119     msSetOutputFormatOption( map->outputformat, "NULLVALUE", value );
2120   }
2121 
2122   msLayerSetProcessingKey(lp, "BANDS", bandlist);
2123   snprintf(numbands, sizeof(numbands), "%d", msCountChars(bandlist, ',')+1);
2124   msSetOutputFormatOption(map->outputformat, "BAND_COUNT", numbands);
2125 
2126   msWCSApplyLayerCreationOptions(lp, map->outputformat, bandlist);
2127 
2128   if( lp->tileindex == NULL && lp->data != NULL &&
2129       strlen(lp->data) > 0 &&
2130       lp->connectiontype != MS_KERNELDENSITY )
2131   {
2132       if( msDrawRasterLayerLowCheckIfMustDraw(map, lp) )
2133       {
2134           char* decrypted_path = NULL;
2135           char szPath[MS_MAXPATHLEN];
2136           hDS = (GDALDatasetH)msDrawRasterLayerLowOpenDataset(
2137                                     map, lp, lp->data, szPath, &decrypted_path);
2138           msFree(decrypted_path);
2139           if( hDS )
2140             msWCSApplyDatasetMetadataAsCreationOptions(lp, map->outputformat, bandlist, hDS);
2141       }
2142       else
2143       {
2144           doDrawRasterLayerDraw = MS_FALSE;
2145       }
2146   }
2147 
2148   free( bandlist );
2149 
2150   if(lp->mask) {
2151     int maskLayerIdx = msGetLayerIndex(map,lp->mask);
2152     layerObj *maskLayer;
2153     outputFormatObj *altFormat;
2154     if(maskLayerIdx == -1) {
2155       msWCSFreeCoverageMetadata(&cm);
2156       msSetError(MS_MISCERR, "Layer (%s) references unknown mask layer (%s)", "msDrawLayer()",
2157                  lp->name,lp->mask);
2158       msDrawRasterLayerLowCloseDataset(lp, hDS);
2159       return msWCSException(map, NULL, NULL, params->version );
2160     }
2161     maskLayer = GET_LAYER(map, maskLayerIdx);
2162     if(!maskLayer->maskimage) {
2163       int i,retcode;
2164       int origstatus, origlabelcache;
2165       char *origImageType = msStrdup(map->imagetype);
2166       altFormat =  msSelectOutputFormat(map, "png24");
2167       msInitializeRendererVTable(altFormat);
2168       /* TODO: check the png24 format hasn't been tampered with, i.e. it's agg */
2169       maskLayer->maskimage= msImageCreate(map->width, map->height, altFormat,
2170                                           map->web.imagepath, map->web.imageurl, map->resolution, map->defresolution, NULL);
2171       if (!maskLayer->maskimage) {
2172         msWCSFreeCoverageMetadata(&cm);
2173         msSetError(MS_MISCERR, "Unable to initialize mask image.", "msDrawLayer()");
2174         msFree(origImageType);
2175         msDrawRasterLayerLowCloseDataset(lp, hDS);
2176         return msWCSException(map, NULL, NULL, params->version );
2177       }
2178 
2179       /*
2180        * force the masked layer to status on, and turn off the labelcache so that
2181        * eventual labels are added to the temporary image instead of being added
2182        * to the labelcache
2183        */
2184       origstatus = maskLayer->status;
2185       origlabelcache = maskLayer->labelcache;
2186       maskLayer->status = MS_ON;
2187       maskLayer->labelcache = MS_OFF;
2188 
2189       /* draw the mask layer in the temporary image */
2190       retcode = msDrawLayer(map, maskLayer, maskLayer->maskimage);
2191       maskLayer->status = origstatus;
2192       maskLayer->labelcache = origlabelcache;
2193       if(retcode != MS_SUCCESS) {
2194         msWCSFreeCoverageMetadata(&cm);
2195         /* set the imagetype from the original outputformat back (it was removed by msSelectOutputFormat() */
2196         msFree(map->imagetype);
2197         map->imagetype = origImageType;
2198         msDrawRasterLayerLowCloseDataset(lp, hDS);
2199         return msWCSException(map, NULL, NULL, params->version );
2200       }
2201       /*
2202        * hack to work around bug #3834: if we have use an alternate renderer, the symbolset may contain
2203        * symbols that reference it. We want to remove those references before the altFormat is destroyed
2204        * to avoid a segfault and/or a leak, and so the the main renderer doesn't pick the cache up thinking
2205        * it's for him.
2206        */
2207       for(i=0; i<map->symbolset.numsymbols; i++) {
2208         if (map->symbolset.symbol[i]!=NULL) {
2209           symbolObj *s = map->symbolset.symbol[i];
2210           if(s->renderer == MS_IMAGE_RENDERER(maskLayer->maskimage)) {
2211             MS_IMAGE_RENDERER(maskLayer->maskimage)->freeSymbol(s);
2212             s->renderer = NULL;
2213           }
2214         }
2215       }
2216       /* set the imagetype from the original outputformat back (it was removed by msSelectOutputFormat() */
2217       msFree(map->imagetype);
2218       map->imagetype = origImageType;
2219 
2220     }
2221   }
2222 
2223   /* create the image object  */
2224   if(!map->outputformat) {
2225     msWCSFreeCoverageMetadata(&cm);
2226     msSetError(MS_WCSERR, "The map outputformat is missing!", "msWCSGetCoverage()");
2227     msDrawRasterLayerLowCloseDataset(lp, hDS);
2228     return msWCSException(map, NULL, NULL, params->version );
2229   } else if( MS_RENDERER_RAWDATA(map->outputformat) || MS_RENDERER_PLUGIN(map->outputformat) ) {
2230     image = msImageCreate(map->width, map->height, map->outputformat, map->web.imagepath, map->web.imageurl, map->resolution, map->defresolution, NULL);
2231   } else {
2232     msWCSFreeCoverageMetadata(&cm);
2233     msSetError(MS_WCSERR, "Map outputformat not supported for WCS!", "msWCSGetCoverage()");
2234     msDrawRasterLayerLowCloseDataset(lp, hDS);
2235     return msWCSException(map, NULL, NULL, params->version );
2236   }
2237 
2238   if( image == NULL ) {
2239     msWCSFreeCoverageMetadata(&cm);
2240     msDrawRasterLayerLowCloseDataset(lp, hDS);
2241     return msWCSException(map, NULL, NULL, params->version );
2242   }
2243   if( MS_RENDERER_RAWDATA(map->outputformat) ) {
2244     if( doDrawRasterLayerDraw ) {
2245       status = msDrawRasterLayerLowWithDataset( map, lp, image, NULL, hDS );
2246     } else {
2247       status = MS_SUCCESS;
2248     }
2249   } else {
2250     status = MS_IMAGE_RENDERER(image)->getRasterBufferHandle(image,&rb);
2251     if(UNLIKELY(status == MS_FAILURE)) {
2252       msWCSFreeCoverageMetadata(&cm);
2253       msDrawRasterLayerLowCloseDataset(lp, hDS);
2254       return MS_FAILURE;
2255     }
2256 
2257     /* Actually produce the "grid". */
2258     if( doDrawRasterLayerDraw ) {
2259       status = msDrawRasterLayerLowWithDataset( map, lp, image, &rb, hDS );
2260     } else {
2261       status = MS_SUCCESS;
2262     }
2263   }
2264   msDrawRasterLayerLowCloseDataset(lp, hDS);
2265 
2266   if( status != MS_SUCCESS ) {
2267     msWCSFreeCoverageMetadata(&cm);
2268     msFreeImage(image);
2269     return msWCSException(map, NULL, NULL, params->version );
2270   }
2271 
2272 
2273   if( strncmp(params->version, "1.1",3) == 0 ) {
2274     msWCSReturnCoverage11( params, map, image );
2275   } else { /* WCS 1.0.0 - just return the binary data with a content type */
2276     const char *fo_filename;
2277 
2278     /* Do we have a predefined filename? */
2279     fo_filename = msGetOutputFormatOption( format, "FILENAME", NULL );
2280     if( fo_filename )
2281       msIO_setHeader("Content-Disposition","attachment; filename=%s",
2282                      fo_filename );
2283 
2284     /* Emit back to client. */
2285     msOutputFormatResolveFromImage( map, image );
2286     msIO_setHeader("Content-Type","%s",MS_IMAGE_MIME_TYPE(map->outputformat));
2287     msIO_sendHeaders();
2288     status = msSaveImage(map, image, NULL);
2289 
2290     if( status != MS_SUCCESS ) {
2291       /* unfortunately, the image content type will have already been sent
2292          but that is hard for us to avoid.  The main error that could happen
2293          here is a misconfigured tmp directory or running out of space. */
2294       msWCSFreeCoverageMetadata(&cm);
2295       return msWCSException(map, NULL, NULL, params->version );
2296     }
2297   }
2298 
2299   /* Cleanup */
2300   msFreeImage(image);
2301   msApplyOutputFormat(&(map->outputformat), NULL, MS_NOOVERRIDE, MS_NOOVERRIDE, MS_NOOVERRIDE);
2302   /* msFreeOutputFormat(format); */
2303 
2304   msWCSFreeCoverageMetadata(&cm);
2305   return status;
2306 }
2307 #endif /* def USE_WCS_SVR */
2308 
2309 /************************************************************************/
2310 /*                           msWCSDispatch()                            */
2311 /*                                                                      */
2312 /*      Entry point for WCS requests                                    */
2313 /************************************************************************/
2314 
msWCSDispatch(mapObj * map,cgiRequestObj * request,owsRequestObj * ows_request)2315 int msWCSDispatch(mapObj *map, cgiRequestObj *request, owsRequestObj *ows_request)
2316 {
2317 #if defined(USE_WCS_SVR)
2318   void *params = NULL; /* either wcsParamsObj* or wcs20ParamsObj* */
2319   int status, retVal, operation;
2320 
2321   /* If SERVICE is not set or not WCS exit gracefully. */
2322   if (ows_request->service == NULL
2323       || !EQUAL(ows_request->service, "WCS")) {
2324     return MS_DONE;
2325   }
2326 
2327   /* If no REQUEST is set, exit with an error */
2328   if (ows_request->request == NULL) {
2329     /* The request has to be set. */
2330     msSetError(MS_WCSERR, "Missing REQUEST parameter",
2331                "msWCSDispatch()");
2332     return msWCSException(map, "MissingParameterValue", "request",
2333                           ows_request->version );
2334   }
2335 
2336   if (EQUAL(ows_request->request, "GetCapabilities")) {
2337     operation = MS_WCS_GET_CAPABILITIES;
2338   } else if (EQUAL(ows_request->request, "DescribeCoverage")) {
2339     operation = MS_WCS_DESCRIBE_COVERAGE;
2340   } else if (EQUAL(ows_request->request, "GetCoverage")) {
2341     operation = MS_WCS_GET_COVERAGE;
2342   } else {
2343     msSetError(MS_WCSERR, "Invalid REQUEST parameter \"%s\"",
2344                "msWCSDispatch()", ows_request->request);
2345     return msWCSException(map, "InvalidParameterValue", "request",
2346                           ows_request->version);
2347   }
2348 
2349   /* Check the number of enabled layers for the REQUEST */
2350   msOWSRequestLayersEnabled(map, "C", ows_request->request, ows_request);
2351   if (ows_request->numlayers == 0) {
2352     int caps_globally_enabled = MS_FALSE, disabled = MS_FALSE;
2353     const char *enable_request;
2354     if(operation == MS_WCS_GET_CAPABILITIES) {
2355       enable_request = msOWSLookupMetadata(&map->web.metadata, "OC", "enable_request");
2356       caps_globally_enabled = msOWSParseRequestMetadata(enable_request, "GetCapabilities", &disabled);
2357     }
2358 
2359     if(caps_globally_enabled == MS_FALSE) {
2360       msSetError(MS_WCSERR, "WCS request not enabled. Check "
2361                  "wcs/ows_enable_request settings.",
2362                  "msWCSDispatch()");
2363       return msWCSException(map, "InvalidParameterValue", "request",
2364                             ows_request->version );
2365     }
2366   }
2367 
2368   /* Check the VERSION parameter */
2369   if (ows_request->version == NULL) {
2370     /* If the VERSION parameter is not set, it is either */
2371     /* an error (Describe and GetCoverage), or it has to */
2372     /* be determined (GetCapabilities). To determine the */
2373     /* version, the request has to be fully parsed to    */
2374     /* obtain the ACCEPTVERSIONS parameter. If this is   */
2375     /* present also, set version to "2.0.1".             */
2376 
2377     if (operation == MS_WCS_GET_CAPABILITIES) {
2378       /* Parse it as if it was a WCS 2.0 request */
2379       wcs20ParamsObjPtr params_tmp = msWCSCreateParamsObj20();
2380       status = msWCSParseRequest20(map, request, ows_request, params_tmp);
2381       if (status == MS_FAILURE) {
2382         msWCSFreeParamsObj20(params_tmp);
2383         return msWCSException(map, "InvalidParameterValue",
2384                               "request", "2.0.1");
2385       }
2386 
2387       /* VERSION negotiation */
2388       if (params_tmp->accept_versions != NULL) {
2389         /* choose highest acceptable */
2390         int i, highest_version = 0;
2391         char version_string[OWS_VERSION_MAXLEN];
2392         for(i = 0; params_tmp->accept_versions[i] != NULL; ++i) {
2393           int version = msOWSParseVersionString(params_tmp->accept_versions[i]);
2394           if (version == OWS_VERSION_BADFORMAT) {
2395             msWCSFreeParamsObj20(params_tmp);
2396             return msWCSException(map, "InvalidParameterValue",
2397                                   "version", NULL );
2398           }
2399           if(version > highest_version) {
2400             highest_version = version;
2401           }
2402         }
2403         msOWSGetVersionString(highest_version, version_string);
2404         params_tmp->version = msStrdup(version_string);
2405         ows_request->version = msStrdup(version_string);
2406       } else {
2407         /* set to highest acceptable */
2408         params_tmp->version = msStrdup("2.0.1");
2409         ows_request->version = msStrdup("2.0.1");
2410       }
2411 
2412       /* check if we can keep the params object */
2413       if (EQUAL(params_tmp->version, "2.0.1")) {
2414         params = params_tmp;
2415       } else {
2416         msWCSFreeParamsObj20(params_tmp);
2417       }
2418     } else { /* operation != GetCapabilities */
2419       /* VERSION is mandatory in other requests */
2420       msSetError(MS_WCSERR, "VERSION parameter not set.",
2421                  "msWCSDispatch()");
2422       return msWCSException(map, "InvalidParameterValue", "version",
2423                             NULL );
2424     }
2425   } else {
2426     /* Parse the VERSION parameter */
2427     int requested_version = msOWSParseVersionString(ows_request->version);
2428     if (requested_version == OWS_VERSION_BADFORMAT) {
2429       /* Return an error if the VERSION is */
2430       /* in an unsupported format.         */
2431       return msWCSException(map, "InvalidParameterValue",
2432                             "version", NULL );
2433     }
2434 
2435     if (operation == MS_WCS_GET_CAPABILITIES) {
2436       /* In case of GetCapabilities, make  */
2437       char version_string[OWS_VERSION_MAXLEN];
2438       int version, supported_versions[] =
2439       {OWS_2_0_1, OWS_2_0_0, OWS_1_1_2, OWS_1_1_1, OWS_1_1_0, OWS_1_0_0};
2440       version = msOWSNegotiateVersion(requested_version,
2441                                       supported_versions,
2442                                       sizeof(supported_versions)/sizeof(int));
2443       msOWSGetVersionString(version, version_string);
2444       msFree(ows_request->version);
2445       ows_request->version = msStrdup(version_string);
2446     }
2447   }
2448 
2449   /* VERSION specific request handler */
2450   if (strcmp(ows_request->version, "1.0.0") == 0
2451       || strcmp(ows_request->version, "1.1.0") == 0
2452       || strcmp(ows_request->version, "1.1.1") == 0
2453       || strcmp(ows_request->version, "1.1.2") == 0) {
2454     params = msWCSCreateParams();
2455     status = msWCSParseRequest(request, params, map);
2456     if (status == MS_FAILURE) {
2457       msWCSFreeParams(params);
2458       free(params);
2459       return msWCSException(map, "InvalidParameterValue",
2460                             "request", "2.0");
2461     }
2462 
2463     retVal = MS_FAILURE;
2464     if (operation == MS_WCS_GET_CAPABILITIES) {
2465       retVal = msWCSGetCapabilities(map, params, request, ows_request);
2466     } else if (operation == MS_WCS_DESCRIBE_COVERAGE) {
2467       retVal = msWCSDescribeCoverage(map, params, ows_request, request);
2468     } else if (operation == MS_WCS_GET_COVERAGE) {
2469       retVal = msWCSGetCoverage(map, request, params, ows_request);
2470     }
2471     msWCSFreeParams(params);
2472     free(params);
2473     return retVal;
2474   } else if (strcmp(ows_request->version, "2.0.0") == 0
2475              || strcmp(ows_request->version, "2.0.1") == 0) {
2476 #if defined(USE_LIBXML2)
2477     int i;
2478 
2479     if (params == NULL) {
2480       params = msWCSCreateParamsObj20();
2481       status = msWCSParseRequest20(map, request, ows_request, params);
2482       if (status == MS_FAILURE) {
2483         msWCSFreeParamsObj20(params);
2484         return msWCSException(map, "InvalidParameterValue",
2485                               "request", "2.0.1");
2486       }
2487       else if (status == MS_DONE) {
2488         /* MS_DONE means, that the exception has already been written to the IO
2489           buffer.
2490         */
2491         msWCSFreeParamsObj20(params);
2492         return MS_FAILURE;
2493       }
2494     }
2495 
2496     /* check if all layer names are valid NCNames */
2497     for(i = 0; i < map->numlayers; ++i) {
2498       if(!msWCSIsLayerSupported(map->layers[i]))
2499         continue;
2500 
2501       /* Check if each layers name is a valid NCName. */
2502       if (msEvalRegex("^[a-zA-z_][a-zA-Z0-9_.-]*$" , map->layers[i]->name) == MS_FALSE) {
2503         msSetError(MS_WCSERR, "Layer name '%s' is not a valid NCName.",
2504                    "msWCSDispatch()", map->layers[i]->name);
2505         msWCSFreeParamsObj20(params);
2506         return msWCSException(map, "mapserv", "Internal", "2.0.1");
2507       }
2508     }
2509 
2510     /* Call operation specific functions */
2511     if (operation == MS_WCS_GET_CAPABILITIES) {
2512       retVal = msWCSGetCapabilities20(map, request, params, ows_request);
2513     } else if (operation == MS_WCS_DESCRIBE_COVERAGE) {
2514       retVal = msWCSDescribeCoverage20(map, params, ows_request);
2515     } else if (operation == MS_WCS_GET_COVERAGE) {
2516       retVal = msWCSGetCoverage20(map, request, params, ows_request);
2517     } else {
2518       msSetError(MS_WCSERR, "Invalid request '%s'.",
2519                  "msWCSDispatch20()", ows_request->request);
2520       retVal = msWCSException20(map, "InvalidParameterValue",
2521                                 "request", "2.0.1");
2522     }
2523     /* clean up */
2524     msWCSFreeParamsObj20(params);
2525     return retVal;
2526 #else /* def USE_LIBXML2 */
2527     msSetError(MS_WCSERR, "WCS 2.0 needs mapserver to be compiled with libxml2.",
2528                "msWCSDispatch()");
2529     return msWCSException(map, "mapserv", "NoApplicableCode", "2.0.1");
2530 #endif /* def USE_LIBXML2 */
2531   } else { /* unsupported version */
2532     msSetError(MS_WCSERR, "WCS Server does not support VERSION %s.",
2533                "msWCSDispatch()", ows_request->version);
2534     return msWCSException(map, "InvalidParameterValue",
2535                           "version", ows_request->version);
2536   }
2537 
2538 #else
2539   msSetError(MS_WCSERR, "WCS server support is not available.", "msWCSDispatch()");
2540   return MS_FAILURE;
2541 #endif
2542 }
2543 
2544 /************************************************************************/
2545 /*                      msWCSGetCoverageMetadata()                      */
2546 /************************************************************************/
2547 
2548 #ifdef USE_WCS_SVR
2549 
msWCSFreeCoverageMetadata(coverageMetadataObj * cm)2550 void msWCSFreeCoverageMetadata(coverageMetadataObj *cm) {
2551   msFree(cm->srs_epsg);
2552 }
2553 
msWCSGetCoverageMetadata(layerObj * layer,coverageMetadataObj * cm)2554 int msWCSGetCoverageMetadata( layerObj *layer, coverageMetadataObj *cm )
2555 {
2556   char  *srs_urn = NULL;
2557   int i = 0;
2558   if ( msCheckParentPointer(layer->map,"map")==MS_FAILURE )
2559     return MS_FAILURE;
2560 
2561   /* -------------------------------------------------------------------- */
2562   /*      Get the SRS in WCS 1.0 format (eg. EPSG:n)                      */
2563   /* -------------------------------------------------------------------- */
2564   msOWSGetEPSGProj(&(layer->projection), &(layer->metadata), "CO", MS_TRUE, &(cm->srs_epsg));
2565   if(!cm->srs_epsg) {
2566     msOWSGetEPSGProj(&(layer->map->projection), &(layer->map->web.metadata), "CO", MS_TRUE, &(cm->srs_epsg));
2567     if(!cm->srs_epsg) {
2568       msSetError(MS_WCSERR, "Unable to determine the SRS for this layer, no projection defined and no metadata available.", "msWCSGetCoverageMetadata()");
2569       return MS_FAILURE;
2570     }
2571   }
2572 
2573   /* -------------------------------------------------------------------- */
2574   /*      Get the SRS in urn format.                                      */
2575   /* -------------------------------------------------------------------- */
2576   if((srs_urn = msOWSGetProjURN(&(layer->projection), &(layer->metadata),
2577                                 "CO", MS_TRUE)) == NULL) {
2578     srs_urn = msOWSGetProjURN(&(layer->map->projection),
2579                               &(layer->map->web.metadata),
2580                               "CO", MS_TRUE);
2581   }
2582 
2583   if( srs_urn != NULL ) {
2584     if( strlen(srs_urn) > sizeof(cm->srs_urn) - 1 ) {
2585       msSetError(MS_WCSERR, "SRS URN too long!",
2586                  "msWCSGetCoverageMetadata()");
2587       return MS_FAILURE;
2588     }
2589 
2590     strcpy( cm->srs_urn, srs_urn );
2591     msFree( srs_urn );
2592   } else
2593     cm->srs_urn[0] = '\0';
2594 
2595   /* -------------------------------------------------------------------- */
2596   /*      If we have "virtual dataset" metadata on the layer, then use    */
2597   /*      that in preference to inspecting the file(s).                   */
2598   /*      We require extent and either size or resolution.                */
2599   /* -------------------------------------------------------------------- */
2600   if( msOWSLookupMetadata(&(layer->metadata), "CO", "extent") != NULL
2601       && (msOWSLookupMetadata(&(layer->metadata), "CO", "resolution") != NULL
2602           || msOWSLookupMetadata(&(layer->metadata), "CO", "size") != NULL) ) {
2603     const char *value;
2604 
2605     /* get extent */
2606     cm->extent.minx = 0.0;
2607     cm->extent.maxx = 0.0;
2608     cm->extent.miny = 0.0;
2609     cm->extent.maxy = 0.0;
2610     if( msOWSGetLayerExtent( layer->map, layer, "CO", &cm->extent ) == MS_FAILURE )
2611       return MS_FAILURE;
2612 
2613     /* get resolution */
2614     cm->xresolution = 0.0;
2615     cm->yresolution = 0.0;
2616     if( (value = msOWSLookupMetadata(&(layer->metadata), "CO", "resolution")) != NULL ) {
2617       char **tokens;
2618       int n;
2619 
2620       tokens = msStringSplit(value, ' ', &n);
2621       if( tokens == NULL || n != 2 ) {
2622         msSetError( MS_WCSERR, "Wrong number of arguments for wcs|ows_resolution metadata.", "msWCSGetCoverageMetadata()");
2623         msFreeCharArray( tokens, n );
2624         return MS_FAILURE;
2625       }
2626       cm->xresolution = atof(tokens[0]);
2627       cm->yresolution = atof(tokens[1]);
2628       msFreeCharArray( tokens, n );
2629     }
2630 
2631     /* get Size (in pixels and lines) */
2632     cm->xsize = 0;
2633     cm->ysize = 0;
2634     if( (value=msOWSLookupMetadata(&(layer->metadata), "CO", "size")) != NULL ) {
2635       char **tokens;
2636       int n;
2637 
2638       tokens = msStringSplit(value, ' ', &n);
2639       if( tokens == NULL || n != 2 ) {
2640         msSetError( MS_WCSERR, "Wrong number of arguments for wcs|ows_size metadata.", "msWCSGetCoverageDomain()");
2641         msFreeCharArray( tokens, n );
2642         return MS_FAILURE;
2643       }
2644       cm->xsize = atoi(tokens[0]);
2645       cm->ysize = atoi(tokens[1]);
2646       msFreeCharArray( tokens, n );
2647     }
2648 
2649     /* try to compute raster size */
2650     if( cm->xsize == 0 && cm->ysize == 0 && cm->xresolution != 0.0 && cm->yresolution != 0.0 && cm->extent.minx != cm->extent.maxx && cm->extent.miny != cm->extent.maxy ) {
2651       cm->xsize = (int) ((cm->extent.maxx - cm->extent.minx) / cm->xresolution + 0.5);
2652       cm->ysize = (int) fabs((cm->extent.maxy - cm->extent.miny) / cm->yresolution + 0.5);
2653     }
2654 
2655     /* try to compute raster resolution */
2656     if( (cm->xresolution == 0.0 || cm->yresolution == 0.0) && cm->xsize != 0 && cm->ysize != 0 ) {
2657       cm->xresolution = (cm->extent.maxx - cm->extent.minx) / cm->xsize;
2658       cm->yresolution = (cm->extent.maxy - cm->extent.miny) / cm->ysize;
2659     }
2660 
2661     /* do we have information to do anything */
2662     if( cm->xresolution == 0.0 || cm->yresolution == 0.0 || cm->xsize == 0 || cm->ysize == 0 ) {
2663       msSetError( MS_WCSERR, "Failed to collect extent and resolution for WCS coverage from metadata for layer '%s'.  Need value wcs|ows_resolution or wcs|ows_size values.", "msWCSGetCoverageMetadata()", layer->name );
2664       return MS_FAILURE;
2665     }
2666 
2667     /* compute geotransform */
2668     cm->geotransform[0] = cm->extent.minx;
2669     cm->geotransform[1] = cm->xresolution;
2670     cm->geotransform[2] = 0.0;
2671     cm->geotransform[3] = cm->extent.maxy;
2672     cm->geotransform[4] = 0.0;
2673     cm->geotransform[5] = -fabs(cm->yresolution);
2674 
2675     /* get bands count, or assume 1 if not found */
2676     cm->bandcount = 1;
2677     if( (value=msOWSLookupMetadata(&(layer->metadata), "CO", "bandcount")) != NULL) {
2678       cm->bandcount = atoi(value);
2679     }
2680 
2681     /* get bands type, or assume float if not found */
2682     cm->imagemode = MS_IMAGEMODE_FLOAT32;
2683     if( (value=msOWSLookupMetadata(&(layer->metadata), "CO", "imagemode")) != NULL ) {
2684       if( EQUAL(value,"INT16") )
2685         cm->imagemode = MS_IMAGEMODE_INT16;
2686       else if( EQUAL(value,"FLOAT32") )
2687         cm->imagemode = MS_IMAGEMODE_FLOAT32;
2688       else if( EQUAL(value,"BYTE") )
2689         cm->imagemode = MS_IMAGEMODE_BYTE;
2690       else {
2691         msSetError( MS_WCSERR, "Content of wcs|ows_imagemode (%s) not recognised.  Should be one of BYTE, INT16 or FLOAT32.", "msWCSGetCoverageMetadata()", value );
2692         return MS_FAILURE;
2693       }
2694     }
2695     /* set color interpretation to undefined */
2696     /* TODO: find better solution */
2697     for(i = 0; i < 10; ++i) {
2698       cm->bandinterpretation[i] = GDALGetColorInterpretationName(GCI_Undefined);
2699     }
2700   } else if( layer->data == NULL ) { /* no virtual metadata, not ok unless we're talking 1 image, hopefully we can fix that */
2701     msSetError( MS_WCSERR, "RASTER Layer with no DATA statement and no WCS virtual dataset metadata.  Tileindexed raster layers not supported for WCS without virtual dataset metadata (cm->extent, wcs_res, wcs_size).", "msWCSGetCoverageDomain()" );
2702     return MS_FAILURE;
2703   } else { /* work from the file (e.g. DATA) */
2704     GDALDatasetH hDS;
2705     GDALRasterBandH hBand;
2706     char szPath[MS_MAXPATHLEN];
2707     char *decrypted_path;
2708 
2709     msGDALInitialize();
2710 
2711     msTryBuildPath3(szPath,  layer->map->mappath, layer->map->shapepath, layer->data);
2712     decrypted_path = msDecryptStringTokens( layer->map, szPath );
2713     if( !decrypted_path )
2714       return MS_FAILURE;
2715 
2716     msAcquireLock( TLOCK_GDAL );
2717     {
2718         char** connectionoptions = msGetStringListFromHashTable(&(layer->connectionoptions));
2719         hDS = GDALOpenEx( decrypted_path, GDAL_OF_RASTER, NULL,
2720                           (const char* const*)connectionoptions, NULL);
2721         CSLDestroy(connectionoptions);
2722     }
2723     if( hDS == NULL ) {
2724       const char *cpl_error_msg = CPLGetLastErrorMsg();
2725 
2726       /* we wish to avoid reporting decrypted paths */
2727       if( cpl_error_msg != NULL
2728           && strstr(cpl_error_msg,decrypted_path) != NULL
2729           && strcmp(decrypted_path,szPath) != 0 )
2730         cpl_error_msg = NULL;
2731 
2732       if( cpl_error_msg == NULL )
2733         cpl_error_msg = "";
2734 
2735       msReleaseLock( TLOCK_GDAL );
2736 
2737       msSetError( MS_IOERR, "%s", "msWCSGetCoverageMetadata()",
2738                   cpl_error_msg );
2739 
2740       msFree( decrypted_path );
2741       return MS_FAILURE;
2742     }
2743     msFree( decrypted_path );
2744 
2745     msGetGDALGeoTransform( hDS, layer->map, layer, cm->geotransform );
2746 
2747     cm->xsize = GDALGetRasterXSize( hDS );
2748     cm->ysize = GDALGetRasterYSize( hDS );
2749 
2750     cm->extent.minx = cm->geotransform[0];
2751     cm->extent.maxx = cm->geotransform[0] + cm->geotransform[1] * cm->xsize + cm->geotransform[2] * cm->ysize;
2752     cm->extent.miny = cm->geotransform[3] + cm->geotransform[4] * cm->xsize + cm->geotransform[5] * cm->ysize;
2753     cm->extent.maxy = cm->geotransform[3];
2754 
2755     cm->xresolution = cm->geotransform[1];
2756     cm->yresolution = cm->geotransform[5];
2757 
2758     /* TODO: need to set resolution */
2759 
2760     cm->bandcount = GDALGetRasterCount( hDS );
2761 
2762     if( cm->bandcount == 0 ) {
2763       msReleaseLock( TLOCK_GDAL );
2764       msSetError( MS_WCSERR, "Raster file %s has no raster bands.  This cannot be used in a layer.", "msWCSGetCoverageMetadata()", layer->data );
2765       return MS_FAILURE;
2766     }
2767 
2768     hBand = GDALGetRasterBand( hDS, 1 );
2769     switch( GDALGetRasterDataType( hBand ) ) {
2770       case GDT_Byte:
2771         cm->imagemode = MS_IMAGEMODE_BYTE;
2772         break;
2773       case GDT_Int16:
2774         cm->imagemode = MS_IMAGEMODE_INT16;
2775         break;
2776       default:
2777         cm->imagemode = MS_IMAGEMODE_FLOAT32;
2778         break;
2779     }
2780 
2781     /* color interpretation */
2782     for(i = 1; i <= 10 && i <= cm->bandcount; ++i) {
2783       GDALColorInterp colorInterp;
2784       hBand = GDALGetRasterBand( hDS, i );
2785       colorInterp = GDALGetRasterColorInterpretation(hBand);
2786       cm->bandinterpretation[i-1] = GDALGetColorInterpretationName(colorInterp);
2787     }
2788 
2789     GDALClose( hDS );
2790     msReleaseLock( TLOCK_GDAL );
2791   }
2792 
2793   /* we must have the bounding box in lat/lon [WGS84(DD)/EPSG:4326] */
2794   cm->llextent = cm->extent;
2795 
2796   /* Already in latlong .. use directly. */
2797   if( layer->projection.proj != NULL && msProjIsGeographicCRS(&(layer->projection))) {
2798     /* no change */
2799   }
2800 
2801   else if (layer->projection.numargs > 0 && !msProjIsGeographicCRS(&(layer->projection))) /* check the layer projection */
2802     msProjectRect(&(layer->projection), NULL, &(cm->llextent));
2803 
2804   else if (layer->map->projection.numargs > 0 && !msProjIsGeographicCRS(&(layer->map->projection))) /* check the map projection */
2805     msProjectRect(&(layer->map->projection), NULL, &(cm->llextent));
2806 
2807   else { /* projection was specified in the metadata only (EPSG:... only at the moment)  */
2808     projectionObj proj;
2809     char projstring[32];
2810 
2811     msInitProjection(&proj); /* or bad things happen */
2812     msProjectionInheritContextFrom(&proj, &(layer->map->projection));
2813 
2814     snprintf(projstring, sizeof(projstring), "init=epsg:%.20s", cm->srs_epsg+5);
2815     if (msLoadProjectionString(&proj, projstring) != 0) return MS_FAILURE;
2816     msProjectRect(&proj, NULL, &(cm->llextent));
2817   }
2818 
2819   return MS_SUCCESS;
2820 }
2821 #endif /* def USE_WCS_SVR */
2822 
2823