1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  MapCache tile caching support file: WMS and OGC forwarding service
6  * Author:   Thomas Bonfort and the MapServer team.
7  *
8  ******************************************************************************
9  * Copyright (c) 1996-2011 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 "mapcache.h"
31 #include <apr_strings.h>
32 #include <math.h>
33 #include "mapcache_services.h"
34 
metadata_xml_add_child(void * rec,const char * key,const char * value)35 static int metadata_xml_add_child(void * rec, const char * key, const char * value)
36 {
37   ezxml_t node = (ezxml_t)rec;
38   ezxml_set_txt(ezxml_add_child(node,key,0),value);
39   return 1;
40 }
41 
sort_strings(const void * pa,const void * pb)42 static int sort_strings(const void* pa, const void* pb)
43 {
44     char** ppszA = (char**)pa;
45     char** ppszB = (char**)pb;
46     return strcmp(*ppszA, *ppszB);
47 }
48 
49 /** \addtogroup services */
50 /** @{ */
51 
_create_capabilities_wms(mapcache_context * ctx,mapcache_request_get_capabilities * req,char * guessed_url,char * path_info,mapcache_cfg * cfg)52 void _create_capabilities_wms(mapcache_context *ctx, mapcache_request_get_capabilities *req, char *guessed_url, char *path_info, mapcache_cfg *cfg)
53 {
54   ezxml_t caps, tmpxml;
55   const char *title;
56   const char *url;
57   ezxml_t capxml;
58   ezxml_t reqxml;
59   ezxml_t vendorxml;
60   ezxml_t toplayer;
61   apr_hash_index_t *grid_index;
62   apr_hash_index_t *tileindex_index;
63   char *tmpcaps;
64   static char *capheader;
65   mapcache_request_get_capabilities_wms *request = (mapcache_request_get_capabilities_wms*)req;
66 #ifdef DEBUG
67   if(request->request.request.type != MAPCACHE_REQUEST_GET_CAPABILITIES) {
68     ctx->set_error(ctx,400,"wrong wms capabilities request");
69     return;
70   }
71 #endif
72 
73   url = apr_table_get(cfg->metadata,"url");
74   if(!url) {
75     url = guessed_url;
76   }
77 
78   url = apr_pstrcat(ctx->pool,url,req->request.service->url_prefix,"?",NULL);
79   caps = ezxml_new("WMT_MS_Capabilities");
80   ezxml_set_attr(caps,"version","1.1.1");
81   /*
82             "<Service>\n"
83               "<Name>OGC:WMS</Name>\n"
84               "<Title>%s</Title>\n"
85               "<OnlineResource xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:href=\"%s/wms?\"/>\n"
86             "</Service>\n"
87   */
88   tmpxml = ezxml_add_child(caps,"Service",0);
89   ezxml_set_txt(ezxml_add_child(tmpxml,"Name",0),"OGC:WMS");
90   title = apr_table_get(cfg->metadata,"title");
91   if(!title) {
92     title = "no title set, add some in metadata";
93   }
94   ezxml_set_txt(ezxml_add_child(tmpxml,"Title",0),title);
95   tmpxml = ezxml_add_child(tmpxml,"OnlineResource",0);
96   ezxml_set_attr(tmpxml,"xmlns:xlink","http://www.w3.org/1999/xlink");
97   ezxml_set_attr(tmpxml,"xlink:href",url);
98   /*
99 
100      "<Capability>\n"
101      "<Request>\n"
102   */
103   capxml = ezxml_add_child(caps,"Capability",0);
104   reqxml = ezxml_add_child(capxml,"Request",0);
105   /*
106      "<GetCapabilities>\n"
107      " <Format>application/vnd.ogc.wms_xml</Format>\n"
108      " <DCPType>\n"
109      "  <HTTP>\n"
110      "   <Get><OnlineResource xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:href=\"%s/wms?\"/></Get>\n"
111      "  </HTTP>\n"
112      " </DCPType>\n"
113      "</GetCapabilities>\n"
114      */
115   tmpxml = ezxml_add_child(reqxml,"GetCapabilities",0);
116   ezxml_set_txt(ezxml_add_child(tmpxml,"Format",0),"application/vnd.ogc.wms_xml");
117   tmpxml = ezxml_add_child(tmpxml,"DCPType",0);
118   tmpxml = ezxml_add_child(tmpxml,"HTTP",0);
119   tmpxml = ezxml_add_child(tmpxml,"Get",0);
120   tmpxml = ezxml_add_child(tmpxml,"OnlineResource",0);
121   ezxml_set_attr(tmpxml,"xmlns:xlink","http://www.w3.org/1999/xlink");
122   ezxml_set_attr(tmpxml,"xlink:href",url);
123 
124   /*
125                 "<GetMap>\n"
126                   "<Format>image/png</Format>\n"
127                   "<Format>image/jpeg</Format>\n"
128                   "<DCPType>\n"
129                     "<HTTP>\n"
130                       "<Get><OnlineResource xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:href=\"%s/wms?\"/></Get>\n"
131                     "</HTTP>\n"
132                   "</DCPType>\n"
133                 "</GetMap>\n"
134   */
135   tmpxml = ezxml_add_child(reqxml,"GetMap",0);
136   ezxml_set_txt(ezxml_add_child(tmpxml,"Format",0),"image/png");
137   ezxml_set_txt(ezxml_add_child(tmpxml,"Format",0),"image/jpeg");
138   tmpxml = ezxml_add_child(tmpxml,"DCPType",0);
139   tmpxml = ezxml_add_child(tmpxml,"HTTP",0);
140   tmpxml = ezxml_add_child(tmpxml,"Get",0);
141   tmpxml = ezxml_add_child(tmpxml,"OnlineResource",0);
142   ezxml_set_attr(tmpxml,"xmlns:xlink","http://www.w3.org/1999/xlink");
143   ezxml_set_attr(tmpxml,"xlink:href",url);
144 
145 
146   /*
147                 "<GetFeatureInfo>\n"
148                   "<Format>text/plain</Format>\n"
149                   "<Format>application/vnd.ogc.gml</Format>\n"
150                   "<DCPType>\n"
151                     "<HTTP>\n"
152                       "<Get>\n"
153                         "<OnlineResource xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:type=\"simple\" xlink:href=\"%s/wms?\" />\n"
154                       "</Get>\n"
155                     "</HTTP>\n"
156                   "</DCPType>\n"
157                 "</GetFeatureInfo>\n"
158   */
159   tmpxml = ezxml_add_child(reqxml,"GetFeatureInfo",0);
160   ezxml_set_txt(ezxml_add_child(tmpxml,"Format",0),"text/plain");
161   ezxml_set_txt(ezxml_add_child(tmpxml,"Format",0),"application/vnd.ogc.gml");
162   tmpxml = ezxml_add_child(tmpxml,"DCPType",0);
163   tmpxml = ezxml_add_child(tmpxml,"HTTP",0);
164   tmpxml = ezxml_add_child(tmpxml,"Get",0);
165   tmpxml = ezxml_add_child(tmpxml,"OnlineResource",0);
166   ezxml_set_attr(tmpxml,"xmlns:xlink","http://www.w3.org/1999/xlink");
167   ezxml_set_attr(tmpxml,"xlink:href",url);
168 
169   /*
170               "<Exception>\n"
171                 "<Format>text/plain</Format>\n"
172               "</Exception>\n"
173   */
174 
175   tmpxml = ezxml_add_child(capxml,"Exception",0);
176   ezxml_set_txt(ezxml_add_child(tmpxml,"Format",0),"text/plain");
177 
178   vendorxml = ezxml_add_child(capxml,"VendorSpecificCapabilities",0);
179   toplayer = ezxml_add_child(capxml,"Layer",0);
180   tmpxml = ezxml_add_child(toplayer,"Title",0);
181   ezxml_set_txt(tmpxml,title);
182 
183   /*
184    * announce all common layer srs's in the root layer.
185    */
186   {
187     int srs_count = (int)apr_hash_count(cfg->grids);
188     int layer_count = (int)apr_hash_count(cfg->tilesets);
189     struct srs_item { char * name; int count; };
190     struct srs_item * srs_list = malloc(srs_count * sizeof(struct srs_item));
191     int srs_iter = 0;
192     int nb_common_srs = 0;
193     int i;
194 
195     // Build list of all layer's SRS
196     grid_index = apr_hash_first(ctx->pool,cfg->grids);
197     while(grid_index) {
198       const void *key;
199       apr_ssize_t keylen;
200       mapcache_grid *grid = NULL;
201       apr_hash_this(grid_index,&key,&keylen,(void**)&grid);
202       srs_list[srs_iter].count = 0;
203       srs_list[srs_iter++].name = grid->srs;
204       grid_index = apr_hash_next(grid_index);
205     }
206     qsort(srs_list, srs_count, sizeof(struct srs_item), sort_strings);
207 
208     // Find out how many tilesets use each SRS
209     tileindex_index = apr_hash_first(ctx->pool,cfg->tilesets);
210     while(tileindex_index) {
211       const void *key;
212       apr_ssize_t keylen;
213       mapcache_tileset *tileset = NULL;
214       apr_hash_this(tileindex_index,&key,&keylen,(void**)&tileset);
215       for(i=0; i<tileset->grid_links->nelts; i++) {
216         mapcache_grid_link *gridlink;
217         mapcache_grid *grid;
218         int j,k;
219         gridlink = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
220         grid = gridlink->grid;
221         for (j=0; j<srs_iter; j++) {
222           if (!strcmp(grid->srs,srs_list[j].name)) {
223             srs_list[j].count++;
224             break;
225           }
226         }
227         for (k=0; k<grid->srs_aliases->nelts; k++) {
228           char * gridalias = APR_ARRAY_IDX(grid->srs_aliases,k,char*);
229           for (j=0; j<srs_iter; j++) {
230             if (!strcmp(gridalias,srs_list[j].name)) {
231               srs_list[j].count++;
232               break;
233             }
234           }
235         }
236       }
237       tileindex_index = apr_hash_next(tileindex_index);
238     }
239 
240     // Output only SRS common to all layers
241     for(i = 0; i < srs_count; i ++) {
242       if (srs_list[i].count == layer_count) {
243         ezxml_set_txt(ezxml_add_child(toplayer,"SRS",0),srs_list[i].name);
244         nb_common_srs++;
245       }
246     }
247     if (nb_common_srs == 0) {
248       ezxml_add_child(toplayer,"SRS",0);
249     }
250     free(srs_list);
251   }
252 
253   tileindex_index = apr_hash_first(ctx->pool,cfg->tilesets);
254 
255   while(tileindex_index) {
256     mapcache_tileset *tileset;
257     ezxml_t layerxml;
258     ezxml_t tsxml;
259     const void *key;
260     apr_ssize_t keylen;
261     const char *title;
262     const char *abstract;
263     const char *keywords;
264     int i;
265     apr_hash_this(tileindex_index,&key,&keylen,(void**)&tileset);
266 
267     if(mapcache_imageio_is_raw_tileset(tileset)) {
268       tileindex_index = apr_hash_next(tileindex_index);
269       continue; /* WMS is not supported for raw layers */
270     }
271 
272     layerxml = ezxml_add_child(toplayer,"Layer",0);
273     ezxml_set_attr(layerxml, "cascaded", "1");
274     ezxml_set_attr(layerxml, "queryable", (tileset->source && tileset->source->info_formats)?"1":"0");
275 
276     ezxml_set_txt(ezxml_add_child(layerxml,"Name",0),tileset->name);
277     tsxml = ezxml_add_child(vendorxml, "TileSet",0);
278 
279     /*optional layer title*/
280     title = apr_table_get(tileset->metadata,"title");
281     if(title) {
282       ezxml_set_txt(ezxml_add_child(layerxml,"Title",0),title);
283     } else {
284       ezxml_set_txt(ezxml_add_child(layerxml,"Title",0),tileset->name);
285     }
286 
287     /*optional layer abstract*/
288     abstract = apr_table_get(tileset->metadata,"abstract");
289     if(abstract) {
290       ezxml_set_txt(ezxml_add_child(layerxml,"Abstract",0),abstract);
291     }
292 
293     // optional layer keywords
294     // `>` suffix in name indicates that a table is expected instead of a string
295     // (see `parseMetadata()` in `configuration_xml.c`)
296     keywords = apr_table_get(tileset->metadata,"keywords>");
297     if (keywords) {
298       apr_table_t * contents = (apr_table_t *)keywords;
299       keywords = apr_table_get(contents,"keyword");
300       if (keywords) {
301         ezxml_t nodeKeywords = ezxml_add_child(layerxml,"KeywordList",0);
302         apr_table_do(metadata_xml_add_child, nodeKeywords, contents, "keyword", NULL);
303       }
304     }
305 
306     if(tileset->wgs84bbox.minx != tileset->wgs84bbox.maxx) {
307       ezxml_t wgsxml = ezxml_add_child(layerxml,"LatLonBoundingBox",0);
308       ezxml_set_attr(wgsxml,"minx",apr_psprintf(ctx->pool,"%f",tileset->wgs84bbox.minx));
309       ezxml_set_attr(wgsxml,"miny",apr_psprintf(ctx->pool,"%f",tileset->wgs84bbox.miny));
310       ezxml_set_attr(wgsxml,"maxx",apr_psprintf(ctx->pool,"%f",tileset->wgs84bbox.maxx));
311       ezxml_set_attr(wgsxml,"maxy",apr_psprintf(ctx->pool,"%f",tileset->wgs84bbox.maxy));
312     }
313 
314     if(tileset->dimensions) {
315       for(i=0; i<tileset->dimensions->nelts; i++) {
316         apr_array_header_t *values;
317         int value_idx;
318         char *dimval = NULL;
319         mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
320         ezxml_t dimxml = ezxml_add_child(layerxml,"Dimension",0);
321         ezxml_set_attr(dimxml,"name",dimension->name);
322         ezxml_set_attr(dimxml,"default",dimension->default_value);
323 
324         if(dimension->unit) {
325           ezxml_set_attr(dimxml,"units",dimension->unit);
326         }
327         values = dimension->get_all_ogc_formatted_entries(ctx,dimension,tileset,NULL,NULL);
328         GC_CHECK_ERROR(ctx);
329         for(value_idx=0;value_idx<values->nelts;value_idx++) {
330           char *idval = APR_ARRAY_IDX(values,value_idx,char*);
331           if(dimval) {
332             dimval = apr_pstrcat(ctx->pool,dimval,",",idval,NULL);
333           } else {
334             dimval = apr_pstrdup(ctx->pool,idval);
335           }
336         }
337         if(dimval) {
338           ezxml_set_txt(dimxml,dimval);
339         }
340       }
341     }
342 
343 
344     for(i=0; i<tileset->grid_links->nelts; i++) {
345       int j;
346       ezxml_t bboxxml;
347       mapcache_grid_link *gridlink = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
348       mapcache_grid *grid = gridlink->grid;
349       mapcache_extent *extent = &(grid->extent);
350       if(gridlink->restricted_extent)
351         extent = gridlink->restricted_extent;
352       bboxxml = ezxml_add_child(layerxml,"BoundingBox",0);
353       ezxml_set_attr(bboxxml,"SRS", grid->srs);
354       ezxml_set_attr(bboxxml,"minx",apr_psprintf(ctx->pool,"%f",extent->minx));
355       ezxml_set_attr(bboxxml,"miny",apr_psprintf(ctx->pool,"%f",extent->miny));
356       ezxml_set_attr(bboxxml,"maxx",apr_psprintf(ctx->pool,"%f",extent->maxx));
357       ezxml_set_attr(bboxxml,"maxy",apr_psprintf(ctx->pool,"%f",extent->maxy));
358       ezxml_set_txt(ezxml_add_child(layerxml,"SRS",0),grid->srs);
359 
360       for(j=0; j<gridlink->grid->srs_aliases->nelts; j++) {
361         ezxml_set_txt(ezxml_add_child(layerxml,"SRS",0),APR_ARRAY_IDX(gridlink->grid->srs_aliases,j,char*));
362       }
363 
364 
365       if(i==0) {
366         char *resolutions;
367         int i;
368         /*wms-c only supports one grid per layer, so we use the first of the tileset's grids */
369         ezxml_set_txt(ezxml_add_child(tsxml,"SRS",0),grid->srs);
370         tmpxml = ezxml_add_child(tsxml,"BoundingBox",0);
371         ezxml_set_attr(tmpxml,"SRS",grid->srs);
372         ezxml_set_attr(tmpxml,"minx",apr_psprintf(ctx->pool,"%f",grid->extent.minx));
373         ezxml_set_attr(tmpxml,"miny",apr_psprintf(ctx->pool,"%f",grid->extent.miny));
374         ezxml_set_attr(tmpxml,"maxx",apr_psprintf(ctx->pool,"%f",grid->extent.maxx));
375         ezxml_set_attr(tmpxml,"maxy",apr_psprintf(ctx->pool,"%f",grid->extent.maxy));
376 
377         resolutions="";
378 
379         for(i=gridlink->minz; i<gridlink->maxz; i++) {
380           resolutions = apr_psprintf(ctx->pool,"%s%.20f ",resolutions,grid->levels[i]->resolution);
381         }
382         ezxml_set_txt(ezxml_add_child(tsxml,"Resolutions",0),resolutions);
383         ezxml_set_txt(ezxml_add_child(tsxml,"Width",0),apr_psprintf(ctx->pool,"%d",grid->tile_sx));
384         ezxml_set_txt(ezxml_add_child(tsxml,"Height",0),apr_psprintf(ctx->pool,"%d", grid->tile_sy));
385       }
386     }
387     if(tileset->format && tileset->format->mime_type) {
388       ezxml_set_txt(ezxml_add_child(tsxml,"Format",0),tileset->format->mime_type);
389     } else {
390       ezxml_set_txt(ezxml_add_child(tsxml,"Format",0),"image/unknown");
391     }
392     ezxml_set_txt(ezxml_add_child(tsxml,"Layers",0),tileset->name);
393     ezxml_set_txt(ezxml_add_child(tsxml,"Styles",0),"");
394     tileindex_index = apr_hash_next(tileindex_index);
395   }
396 
397 
398   tmpcaps = ezxml_toxml(caps);
399   ezxml_free(caps);
400   capheader=
401     "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\
402 <!DOCTYPE WMT_MS_Capabilities SYSTEM \"http://schemas.opengis.net/wms/1.1.0/capabilities_1_1_0.dtd\"\
403 [\
404  <!ELEMENT VendorSpecificCapabilities EMPTY>\
405 ]>\n";
406   request->request.capabilities = apr_pstrcat(ctx->pool,capheader,tmpcaps,NULL);
407   free(tmpcaps);
408   request->request.mime_type = apr_pstrdup(ctx->pool,"text/xml");
409 }
410 
_lookup_auto_projection(mapcache_context * ctx,const char * srs)411 static char *_lookup_auto_projection(mapcache_context *ctx, const char *srs) {
412   if(!strcasecmp(srs,"auto:42001")) {
413     char *srsdup = apr_pstrdup(ctx->pool,srs);
414     char *lon = strchr(srsdup,','),*lat;
415     int nLon,nLat;
416     if(!lon) return srsdup;
417     lon = strchr(lon+1,',');
418     if(!lon) return srsdup;
419     lon++;
420     lat = strchr(lon,',');
421     if(!lat) return srsdup;
422     *lat = 0;
423     lat++;
424     nLon = (int)(floor( (atof(lon) + 180.0) / 6.0 ))*6 + 3 - 180;
425     nLat = (atof(lat)>=0)?45:-45;
426     return apr_psprintf(ctx->pool,"auto:42001,9001,%d,%d",nLon,nLat);
427   }
428   return (char*)srs;
429 }
430 
431 /**
432  * \brief parse a WMS request
433  * \private \memberof mapcache_service_wms
434  * \sa mapcache_service::parse_request()
435  */
_mapcache_service_wms_parse_request(mapcache_context * ctx,mapcache_service * this,mapcache_request ** request,const char * pathinfo,apr_table_t * params,mapcache_cfg * config)436 void _mapcache_service_wms_parse_request(mapcache_context *ctx, mapcache_service *this, mapcache_request **request,
437     const char *pathinfo, apr_table_t *params, mapcache_cfg *config)
438 {
439   const char *str = NULL;
440   const char *srs=NULL;
441   int width=0, height=0;
442   double *tmpbbox;
443   mapcache_extent extent;
444   int isGetMap=0,iswms130=0;
445   int errcode = 200;
446   char *errmsg = NULL;
447   mapcache_service_wms *wms_service = (mapcache_service_wms*)this;
448 
449   *request = NULL;
450 
451   str = apr_table_get(params,"SERVICE");
452   if(!str) {
453     /* service is optional if we have a getmap */
454     str = apr_table_get(params,"REQUEST");
455     if(!str) {
456       errcode = 400;
457       errmsg = "received wms with no service and request";
458       ctx->service = NULL;
459       goto proxies;
460     }
461   } else if( strcasecmp(str,"wms") ) {
462     errcode = 400;
463     errmsg = apr_psprintf(ctx->pool,"received wms request with invalid service param %s", str);
464     goto proxies;
465   }
466 
467   str = apr_table_get(params,"REQUEST");
468   if(!str) {
469     errcode = 400;
470     errmsg = "received wms with no request";
471     goto proxies;
472   }
473 
474   if( ! strcasecmp(str,"getmap")) {
475     isGetMap = 1;
476     str = apr_table_get(params,"VERSION");
477     if(str && !strcmp(str,"1.3.0")) {
478       iswms130 = 1;
479     }
480   } else {
481     if( ! strcasecmp(str,"getcapabilities") ) {
482       *request = (mapcache_request*)
483                  apr_pcalloc(ctx->pool,sizeof(mapcache_request_get_capabilities_wms));
484       (*request)->type = MAPCACHE_REQUEST_GET_CAPABILITIES;
485       goto proxies; /* OK */
486     } else if( ! strcasecmp(str,"getfeatureinfo") ) {
487       //nothing
488     } else {
489       errcode = 501;
490       errmsg = apr_psprintf(ctx->pool,"received wms with invalid request %s",str);
491       goto proxies;
492     }
493   }
494 
495 
496   str = apr_table_get(params,"BBOX");
497   if(!str) {
498     errcode = 400;
499     errmsg = "received wms request with no bbox";
500     goto proxies;
501   } else {
502     int nextents;
503     if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, str,",",&tmpbbox,&nextents) ||
504         nextents != 4) {
505       errcode = 400;
506       errmsg = "received wms request with invalid bbox";
507       goto proxies;
508     }
509     extent.minx = tmpbbox[0];
510     extent.miny = tmpbbox[1];
511     extent.maxx = tmpbbox[2];
512     extent.maxy = tmpbbox[3];
513   }
514 
515   str = apr_table_get(params,"WIDTH");
516   if(!str) {
517     errcode = 400;
518     errmsg = "received wms request with no width";
519     goto proxies;
520   } else {
521     char *endptr;
522     width = (int)strtol(str,&endptr,10);
523     if(*endptr != 0 || width <= 0) {
524       errcode = 400;
525       errmsg = "received wms request with invalid width";
526       goto proxies;
527     }
528   }
529 
530   str = apr_table_get(params,"HEIGHT");
531   if(!str) {
532     errcode = 400;
533     errmsg = "received wms request with no height";
534     goto proxies;
535   } else {
536     char *endptr;
537     height = (int)strtol(str,&endptr,10);
538     if(*endptr != 0 || height <= 0) {
539       errcode = 400;
540       errmsg = "received wms request with invalid height";
541       goto proxies;
542     }
543   }
544 
545   if(width > wms_service->maxsize || height > wms_service->maxsize) {
546     errcode=400;
547     errmsg = "received wms request with width or height over configured maxsize limit";
548     goto proxies;
549   }
550 
551   if(iswms130) {
552     srs = apr_table_get(params,"CRS");
553     if(!srs) {
554       errcode = 400;
555       errmsg = "received wms request with no crs";
556       goto proxies;
557     }
558   } else {
559     srs = apr_table_get(params,"SRS");
560     if(!srs) {
561       errcode = 400;
562       errmsg = "received wms request with no srs";
563       goto proxies;
564     }
565   }
566   if(iswms130) {
567     /*check if we should flip the axis order*/
568     if(mapcache_is_axis_inverted(srs)) {
569       double swap;
570       swap = extent.minx;
571       extent.minx = extent.miny;
572       extent.miny = swap;
573       swap = extent.maxx;
574       extent.maxx = extent.maxy;
575       extent.maxy = swap;
576     }
577   }
578 
579   if(isGetMap) {
580     str = apr_table_get(params,"LAYERS");
581     if(!str) {
582       errcode = 400;
583       errmsg = "received wms request with no layers";
584       goto proxies;
585     } else {
586       char *last, *layers;
587       const char *key;
588       int count=1;
589       int i,layeridx;
590       int x,y,z;
591       mapcache_request_get_map *map_req = NULL;
592       mapcache_request_get_tile *tile_req = NULL;
593       mapcache_grid_link *main_grid_link = NULL;
594       mapcache_tileset *main_tileset = NULL;
595       mapcache_request_type type;
596       mapcache_image_format *imf;
597 
598       /* count the number of layers that are requested.
599        * if we are in combined-mirror mode, then there is
600        * always a single layer */
601       if(config->mode != MAPCACHE_MODE_MIRROR_COMBINED) {
602         for(key=str; *key; key++) if(*key == ',') count++;
603       }
604 
605       /*
606        * look to see if we have a getTile or a getMap request. We do this by looking at the first
607        * wms layer that was provided in the request.
608        * Checking to see if all requested layers reference the grid will be done in a second step
609        */
610       type = MAPCACHE_REQUEST_GET_TILE;
611 
612       if(count ==1 || config->mode == MAPCACHE_MODE_MIRROR_COMBINED) {
613         key = str;
614       } else {
615         layers = apr_pstrdup(ctx->pool,str);
616         key = apr_strtok(layers, ",", &last); /* extract first layer */
617       }
618       main_tileset = mapcache_configuration_get_tileset(config,key);
619       if(!main_tileset || mapcache_imageio_is_raw_tileset(main_tileset)) {
620         errcode = 404;
621         errmsg = apr_psprintf(ctx->pool,"received wms request with invalid layer %s", key);
622         goto proxies;
623       }
624       if(config->mode != MAPCACHE_MODE_NORMAL) {
625         main_tileset = mapcache_tileset_clone(ctx,main_tileset);
626         main_tileset->name = (char*)key;
627       }
628 
629       srs = _lookup_auto_projection(ctx,srs);
630 
631       for(i=0; i<main_tileset->grid_links->nelts; i++) {
632         mapcache_grid_link *sgrid = APR_ARRAY_IDX(main_tileset->grid_links,i,mapcache_grid_link*);
633         /* look for a grid with a matching srs */
634         if(strcasecmp(sgrid->grid->srs,srs)) {
635           /* look if the grid has some srs aliases */
636           int s;
637           for(s=0; s<sgrid->grid->srs_aliases->nelts; s++) {
638             char *srsalias = APR_ARRAY_IDX(sgrid->grid->srs_aliases,s,char*);
639             if(!strcasecmp(srsalias,srs)) break;
640           }
641           if(s==sgrid->grid->srs_aliases->nelts)
642             continue; /* no srs alias matches the requested srs */
643         }
644         main_grid_link = sgrid;
645         break;
646       }
647       if(!main_grid_link) {
648         errcode = 400;
649         errmsg = apr_psprintf(ctx->pool,
650                               "received unsuitable wms request: no <grid> with suitable srs found for layer %s",main_tileset->name);
651         goto proxies;
652       }
653 
654       /* verify we align on the tileset's grid */
655       if(main_grid_link->grid->tile_sx != width || main_grid_link->grid->tile_sy != height ||
656           mapcache_grid_get_cell(ctx, main_grid_link->grid, &extent, &x,&y,&z) != MAPCACHE_SUCCESS) {
657         /* we have the correct srs, but the request does not align on the grid */
658         type = MAPCACHE_REQUEST_GET_MAP;
659       }
660 
661       imf = wms_service->getmap_format;
662       if(wms_service->allow_format_override) {
663         str = apr_table_get(params,"FORMAT");
664         if(strcmp(str,imf->name) && strcmp(str,imf->mime_type)) {
665           apr_hash_index_t *hi;
666           for (hi = apr_hash_first(ctx->pool, ctx->config->image_formats); hi; hi = apr_hash_next(hi)) {
667             apr_hash_this(hi, NULL, NULL, (void**)&imf);
668             if(!strcmp(imf->name, str) || (imf->mime_type && !strcmp(imf->mime_type, str))) {
669               break;
670             }
671           }
672           if(!hi) { /* did not find any matching format for given mimetype or name */
673             errcode = 404;
674             errmsg = apr_psprintf(ctx->pool,"received wms request with invalid format %s", str);
675             goto proxies;
676           }
677         }
678       }
679 
680 
681       if(type == MAPCACHE_REQUEST_GET_TILE) {
682         tile_req = apr_pcalloc(ctx->pool, sizeof(mapcache_request_get_tile));
683         tile_req->tiles = apr_pcalloc(ctx->pool, count*sizeof(mapcache_tile*));
684         tile_req->image_request.format = imf;
685         *request = (mapcache_request*)tile_req;
686         (*request)->type = MAPCACHE_REQUEST_GET_TILE;
687       } else {
688         map_req = apr_pcalloc(ctx->pool, sizeof(mapcache_request_get_map));
689         map_req->maps = apr_pcalloc(ctx->pool, count*sizeof(mapcache_map*));
690         map_req->getmap_strategy = wms_service->getmap_strategy;
691         map_req->resample_mode = wms_service->resample_mode;
692         map_req->image_request.format = imf;
693         *request = (mapcache_request*)map_req;
694         (*request)->type = MAPCACHE_REQUEST_GET_MAP;
695       }
696 
697 
698       /*
699        * loop through all the layers to verify that they reference the requested grid,
700        * and to extract any dimensions if configured
701        */
702       if(count>1)
703         layers = apr_pstrdup(ctx->pool,str); /* apr_strtok modifies its input string */
704 
705       for (layeridx=0,key = ((count==1)?str:apr_strtok(layers, ",", &last)); key != NULL;
706            key = ((count==1)?NULL:apr_strtok(NULL, ",", &last)),layeridx++) {
707         int i;
708         mapcache_tileset *tileset = main_tileset;
709         mapcache_grid_link *grid_link = main_grid_link;
710         apr_array_header_t *dimtable = NULL;
711 
712         if(layeridx) {
713           /*
714            * if we have multiple requested layers, check that they reference the requested grid
715            * this step is not done for the first tileset as we have already performed it
716            */
717           tileset = mapcache_configuration_get_tileset(config,key);
718           if (!tileset || mapcache_imageio_is_raw_tileset(tileset)) {
719             errcode = 404;
720             errmsg = apr_psprintf(ctx->pool,"received wms request with invalid layer %s", key);
721             goto proxies;
722           }
723           if(config->mode != MAPCACHE_MODE_NORMAL) {
724             tileset = mapcache_tileset_clone(ctx,tileset);
725             tileset->name = (char*)key;
726           }
727           grid_link = NULL;
728           for(i=0; i<tileset->grid_links->nelts; i++) {
729             grid_link = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
730             if(grid_link->grid == main_grid_link->grid) {
731               break;
732             }
733           }
734           if(i==tileset->grid_links->nelts) {
735             /* the tileset does not reference the grid of the first tileset */
736             errcode = 400;
737             errmsg = apr_psprintf(ctx->pool,
738                                   "tileset %s does not reference grid %s (referenced by tileset %s)",
739                                   tileset->name, main_grid_link->grid->name,main_tileset->name);
740             goto proxies;
741           }
742         }
743         if(type == MAPCACHE_REQUEST_GET_TILE) {
744           mapcache_tile *tile = mapcache_tileset_tile_create(ctx->pool, tileset, grid_link);
745           tile->x = x;
746           tile->y = y;
747           tile->z = z;
748           mapcache_tileset_tile_validate(ctx,tile);
749           if(GC_HAS_ERROR(ctx)) {
750             /* don't bail out just yet, in case multiple tiles have been requested */
751             ctx->clear_errors(ctx);
752           } else {
753             tile_req->tiles[tile_req->ntiles++] = tile;
754           }
755           dimtable = tile->dimensions;
756 
757         } else {
758           mapcache_map *map = mapcache_tileset_map_create(ctx->pool,tileset,grid_link);
759           map->width = width;
760           map->height = height;
761           map->extent = extent;
762           map_req->maps[map_req->nmaps++] = map;
763           dimtable = map->dimensions;
764         }
765 
766         /*look for dimensions*/
767         if(dimtable) {
768           const char *value;
769           if(tileset->dimensions) {
770             for(i=0; i<tileset->dimensions->nelts; i++) {
771               char *dim_name;
772               mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
773               if(!strcasecmp(dimension->name,"TIME") || !strcasecmp(dimension->name,"ELEVATION")) {
774                 dim_name = dimension->name;
775               } else {
776                 dim_name = apr_pstrcat(ctx->pool, "dim_", dimension->name, NULL);
777               }
778               if((value = (char*)apr_table_get(params,dim_name)) == NULL) {
779                 if(strcasecmp(dimension->name,"TIME") && strcasecmp(dimension->name,"ELEVATION")) {
780                   /* also test for the dimension without the DIM_ prefix if the latter was not found in the KVP params */
781                   dim_name = dimension->name;
782                   value = (char*)apr_table_get(params,dim_name);
783                 }
784               }
785 
786               if(value) {
787                 mapcache_set_requested_dimension(ctx,dimtable,dimension->name,value);
788                 GC_CHECK_ERROR(ctx);
789               }
790             }
791           }
792         }
793       }
794       if(tile_req && tile_req->ntiles == 0) {
795         errcode = 404;
796         errmsg = "request for tile outside of restricted extent";
797         goto proxies;
798       }
799     }
800   } else {
801     int i;
802     int x,y;
803     mapcache_grid_link *grid_link;
804     mapcache_feature_info *fi;
805     mapcache_request_get_feature_info *req_fi;
806     //getfeatureinfo
807     str = apr_table_get(params,"QUERY_LAYERS");
808     if(!str) {
809       errcode = 400;
810       errmsg = "received wms getfeatureinfo request with no query layers";
811       goto proxies;
812     } else if(strstr(str,",")) {
813       errcode = 501;
814       errmsg = "wms getfeatureinfo not implemented for multiple layers";
815       goto proxies;
816     } else {
817       mapcache_tileset *tileset = mapcache_configuration_get_tileset(config,str);
818       if(!tileset || mapcache_imageio_is_raw_tileset(tileset)) {
819         errcode = 404;
820         errmsg = apr_psprintf(ctx->pool,"received wms getfeatureinfo request with invalid layer %s", str);
821         goto proxies;
822       }
823       if(!tileset->source || !tileset->source->info_formats) {
824         errcode = 404;
825         errmsg = apr_psprintf(ctx->pool,"received wms getfeatureinfo request for unqueryable layer %s", str);
826         goto proxies;
827       }
828 
829       grid_link = NULL;
830       for(i=0; i<tileset->grid_links->nelts; i++) {
831         mapcache_grid_link *sgrid = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
832         if(strcasecmp(sgrid->grid->srs,srs)) continue;
833         grid_link = sgrid;
834         break;
835       }
836       if(!grid_link) {
837         errcode = 400;
838         errmsg = apr_psprintf(ctx->pool,
839                               "received unsuitable wms request: no <grid> with suitable srs found for layer %s",tileset->name);
840         goto proxies;
841       }
842 
843       str = apr_table_get(params,"X");
844       if(!str) {
845         errcode = 400;
846         errmsg = "received wms getfeatureinfo request with no X";
847         goto proxies;
848       } else {
849         char *endptr;
850         x = (int)strtol(str,&endptr,10);
851         if(*endptr != 0 || x <= 0 || x>=width) {
852           errcode = 400;
853           errmsg = "received wms request with invalid X";
854           goto proxies;
855         }
856       }
857 
858       str = apr_table_get(params,"Y");
859       if(!str) {
860         errcode = 400;
861         errmsg = "received wms getfeatureinfo request with no Y";
862         goto proxies;
863       } else {
864         char *endptr;
865         y = (int)strtol(str,&endptr,10);
866         if(*endptr != 0 || y <= 0 || y>=height) {
867           errcode = 400;
868           errmsg = "received wms request with invalid Y";
869           goto proxies;
870         }
871       }
872 
873       fi = mapcache_tileset_feature_info_create(ctx->pool, tileset, grid_link);
874       fi->i = x;
875       fi->j = y;
876       fi->format = apr_pstrdup(ctx->pool,apr_table_get(params,"INFO_FORMAT"));
877       if(!fi->format) {
878         errcode = 400;
879         errmsg = "received wms getfeatureinfo request with no INFO_FORMAT";
880         goto proxies;
881       }
882 
883       if(fi->map.dimensions) {
884         int i;
885         for(i=0; i<tileset->dimensions->nelts; i++) {
886           mapcache_dimension *dimension = APR_ARRAY_IDX(tileset->dimensions,i,mapcache_dimension*);
887           const char *value;
888           if((value = (char*)apr_table_get(params,dimension->name)) != NULL) {
889             mapcache_map_set_cached_dimension(ctx,&fi->map,dimension->name,value);
890             GC_CHECK_ERROR(ctx);
891           }
892         }
893       }
894       fi->map.width = width;
895       fi->map.height = height;
896       fi->map.extent = extent;
897       req_fi = apr_pcalloc(ctx->pool, sizeof(mapcache_request_get_feature_info));
898       req_fi->request.type = MAPCACHE_REQUEST_GET_FEATUREINFO;
899       req_fi->fi = fi;
900       *request = (mapcache_request*)req_fi;
901 
902     }
903   }
904 
905 proxies:
906   /*
907    * if we don't have a gettile or getmap request we can treat from the cache tiles, look to see if we have a rule
908    * that tells us to forward the request somewhere else
909    */
910   if(errcode == 200 &&
911       *request && (
912         /* if its a single tile we're ok*/
913         ((*request)->type == MAPCACHE_REQUEST_GET_TILE && ((mapcache_request_get_tile*)(*request))->ntiles == 1) ||
914         ((*request)->type == MAPCACHE_REQUEST_GET_FEATUREINFO) ||
915 
916         /* if we have a getmap or multiple tiles, we must check that assembling is allowed */
917         (((*request)->type == MAPCACHE_REQUEST_GET_MAP || (
918             (*request)->type == MAPCACHE_REQUEST_GET_TILE && ((mapcache_request_get_tile*)(*request))->ntiles > 1)) &&
919          wms_service->getmap_strategy == MAPCACHE_GETMAP_ASSEMBLE)
920       )) {
921     /* if we're here, then we have succesfully parsed the request and can treat it ourselves, i.e. from cached tiles */
922     return;
923   } else {
924     /* look to see if we can proxy the request somewhere*/
925     int i,j;
926     for(i=0; i<wms_service->forwarding_rules->nelts; i++) {
927       mapcache_forwarding_rule *rule = APR_ARRAY_IDX(wms_service->forwarding_rules,i,mapcache_forwarding_rule*);
928       int got_a_match = 1;
929       for(j=0; j<rule->match_params->nelts; j++) {
930         mapcache_dimension *match_param = APR_ARRAY_IDX(rule->match_params,j,mapcache_dimension*);
931         const char *value = apr_table_get(params,match_param->name);
932         if(!value || match_param->_get_entries_for_value(ctx,match_param,value,NULL,NULL,NULL)->nelts == 0) {
933           /* the parameter was not supplied, or did not validate: we don't apply this rule */
934           ctx->clear_errors(ctx);
935           got_a_match = 0;
936           break;
937         }
938       }
939       if( got_a_match == 1 ) {
940         mapcache_request_proxy *req_proxy = apr_pcalloc(ctx->pool,sizeof(mapcache_request_proxy));
941         *request = (mapcache_request*)req_proxy;
942         (*request)->service = this;
943         (*request)->type = MAPCACHE_REQUEST_PROXY;
944         req_proxy->rule = rule;
945         req_proxy->params = params;
946         if(rule->append_pathinfo) {
947           req_proxy->pathinfo = pathinfo;
948         } else {
949           req_proxy->pathinfo = NULL;
950         }
951         return;
952       }
953     }
954   }
955 
956   /*
957    * if we are here, then we are either in the getfeatureinfo / getcapabilities / getmap case,
958    * or there was an error parsing the request and no rules to proxy it elsewhere
959    */
960   if(errcode != 200) {
961     ctx->set_error(ctx,errcode,errmsg);
962     return;
963   }
964 #ifdef DEBUG
965   if((*request)->type != MAPCACHE_REQUEST_GET_TILE &&
966       (*request)->type != MAPCACHE_REQUEST_GET_MAP &&
967       (*request)->type != MAPCACHE_REQUEST_GET_FEATUREINFO &&
968       (*request)->type != MAPCACHE_REQUEST_GET_CAPABILITIES) {
969     ctx->set_error(ctx,500,"BUG: request not gettile or getmap");
970     return;
971   }
972 #endif
973 }
974 
975 
_configuration_parse_wms_xml(mapcache_context * ctx,ezxml_t node,mapcache_service * gservice,mapcache_cfg * cfg)976 void _configuration_parse_wms_xml(mapcache_context *ctx, ezxml_t node, mapcache_service *gservice, mapcache_cfg *cfg)
977 {
978   mapcache_service_wms *wms = (mapcache_service_wms*)gservice;
979   ezxml_t rule_node;
980   assert(gservice->type == MAPCACHE_SERVICE_WMS);
981 
982   for( rule_node = ezxml_child(node,"forwarding_rule"); rule_node; rule_node = rule_node->next) {
983     mapcache_forwarding_rule *rule;
984     ezxml_t node;
985     char *name = (char*)ezxml_attr(rule_node,"name");
986     if(!name) name = "(null)";
987     rule = apr_pcalloc(ctx->pool, sizeof(mapcache_forwarding_rule));
988     rule->name = apr_pstrdup(ctx->pool,name);
989     rule->match_params = apr_array_make(ctx->pool,1,sizeof(mapcache_dimension*));
990     rule->max_post_len = 10485760; /* 10 megabytes by default */
991 
992     node = ezxml_child(rule_node,"append_pathinfo");
993     if(node && !strcasecmp(node->txt,"true")) {
994       rule->append_pathinfo = 1;
995     } else {
996       rule->append_pathinfo = 0;
997     }
998 
999     node = ezxml_child(rule_node,"max_post_length");
1000     if(node) {
1001       char *endptr;
1002       rule->max_post_len= (size_t)strtol(node->txt,&endptr,10);
1003       if(*endptr != 0 || rule->max_post_len <= 0) {
1004         ctx->set_error(ctx,500,"rule \"%s\" cannot have a negative or null <max_post_length>",name);
1005         return;
1006       }
1007     }
1008 
1009     node = ezxml_child(rule_node,"http");
1010     if(!node) {
1011       ctx->set_error(ctx,500,"rule \"%s\" does not contain an <http> block",name);
1012       return;
1013     }
1014     rule->http = mapcache_http_configuration_parse_xml(ctx,node);
1015     GC_CHECK_ERROR(ctx);
1016 
1017     for(node = ezxml_child(rule_node,"param"); node; node = node->next) {
1018       char *name = (char*)ezxml_attr(node,"name");
1019       char *type = (char*)ezxml_attr(node,"type");
1020 
1021       mapcache_dimension *dimension = NULL;
1022 
1023       if(!name || !strlen(name)) {
1024         ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in forwarding rule <param>");
1025         return;
1026       }
1027 
1028       if(type && *type) {
1029         if(!strcmp(type,"values")) {
1030           dimension = mapcache_dimension_values_create(ctx,ctx->pool);
1031         } else if(!strcmp(type,"regex")) {
1032           dimension = mapcache_dimension_regex_create(ctx,ctx->pool);
1033         } else {
1034           ctx->set_error(ctx,400,"unknown <param> type \"%s\". expecting \"values\" or \"regex\".",type);
1035           return;
1036         }
1037         dimension->class_name = "param";
1038       } else {
1039         ctx->set_error(ctx,400, "mandatory attribute \"type\" not found in <dimensions>");
1040         return;
1041       }
1042       GC_CHECK_ERROR(ctx);
1043 
1044       dimension->name = apr_pstrdup(ctx->pool,name);
1045 
1046       dimension->configuration_parse_xml(ctx,dimension,node);
1047       GC_CHECK_ERROR(ctx);
1048 
1049       APR_ARRAY_PUSH(rule->match_params,mapcache_dimension*) = dimension;
1050     }
1051     APR_ARRAY_PUSH(wms->forwarding_rules,mapcache_forwarding_rule*) = rule;
1052   }
1053   if ((rule_node = ezxml_child(node,"full_wms")) != NULL) {
1054     if(!strcmp(rule_node->txt,"assemble")) {
1055       wms->getmap_strategy = MAPCACHE_GETMAP_ASSEMBLE;
1056     } else if(!strcmp(rule_node->txt,"forward")) {
1057       wms->getmap_strategy = MAPCACHE_GETMAP_FORWARD;
1058     } else if(*rule_node->txt && strcmp(rule_node->txt,"error")) {
1059       ctx->set_error(ctx,400, "unknown value %s for node <full_wms> (allowed values: assemble, getmap or error", rule_node->txt);
1060       return;
1061     }
1062   }
1063 
1064   wms->getmap_format = mapcache_configuration_get_image_format(cfg,"JPEG");
1065   if ((rule_node = ezxml_child(node,"format")) != NULL) {
1066     const char *attr;
1067     wms->getmap_format = mapcache_configuration_get_image_format(cfg,rule_node->txt);
1068     if(!wms->getmap_format) {
1069       ctx->set_error(ctx,400, "unknown <format> %s for wms service", rule_node->txt);
1070       return;
1071     }
1072     attr = ezxml_attr(rule_node,"allow_client_override");
1073     if(attr && !strcmp(attr,"true")) {
1074       wms->allow_format_override = 1;
1075     }
1076   }
1077 
1078   if ((rule_node = ezxml_child(node,"resample_mode")) != NULL) {
1079     if(!strcmp(rule_node->txt,"nearest")) {
1080       wms->resample_mode = MAPCACHE_RESAMPLE_NEAREST;
1081     } else if(!strcmp(rule_node->txt,"bilinear")) {
1082       wms->resample_mode = MAPCACHE_RESAMPLE_BILINEAR;
1083     } else {
1084       ctx->set_error(ctx,400, "unknown value %s for node <resample_mode> (allowed values: nearest, bilinear", rule_node->txt);
1085       return;
1086     }
1087   }
1088 
1089   if ((rule_node = ezxml_child(node,"maxsize")) != NULL) {
1090     wms->maxsize = atoi(rule_node->txt);
1091     if(wms->maxsize <= 0) {
1092       ctx->set_error(ctx,400, "failed to parse wms service maxsize value \"%s\"", rule_node->txt);
1093       return;
1094     }
1095   }
1096 }
1097 
_format_error_wms(mapcache_context * ctx,mapcache_service * service,char * msg,char ** err_body,apr_table_t * headers)1098 void _format_error_wms(mapcache_context *ctx, mapcache_service *service, char *msg,
1099                        char **err_body, apr_table_t *headers)
1100 {
1101   char *template = "\
1102 <?xml version='1.0' encoding=\"UTF-8\" standalone=\"no\" ?>\n\
1103 <!DOCTYPE ServiceExceptionReport SYSTEM \
1104 \"http://schemas.opengis.net/wms/1.1.1/exception_1_1_1.dtd\">\n\
1105 <ServiceExceptionReport version=\"1.1.1\">\n\
1106 <ServiceException>\n\
1107 %s\n\
1108 </ServiceException>\n\
1109 %s\
1110 </ServiceExceptionReport>";
1111 
1112   char *exceptions="";
1113 
1114   if(ctx->exceptions) {
1115     const apr_array_header_t *array = apr_table_elts(ctx->exceptions);
1116     apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
1117     int i;
1118     for (i = 0; i < array->nelts; i++) {
1119       exceptions = apr_pstrcat(ctx->pool,exceptions,apr_psprintf(ctx->pool,
1120                                "<ServiceException code=\"%s\"><![CDATA[%s]]></ServiceException>\n",elts[i].key,elts[i].val),NULL);
1121     }
1122   }
1123 
1124   *err_body = apr_psprintf(ctx->pool,template,
1125                            mapcache_util_str_xml_escape(ctx->pool, msg, MAPCACHE_UTIL_XML_SECTION_TEXT),
1126                            exceptions);
1127   apr_table_set(headers, "Content-Type", "application/vnd.ogc.se_xml");
1128 }
1129 
mapcache_service_wms_create(mapcache_context * ctx)1130 mapcache_service* mapcache_service_wms_create(mapcache_context *ctx)
1131 {
1132   mapcache_service_wms* service = (mapcache_service_wms*)apr_pcalloc(ctx->pool, sizeof(mapcache_service_wms));
1133   if(!service) {
1134     ctx->set_error(ctx, 500, "failed to allocate wms service");
1135     return NULL;
1136   }
1137   service->forwarding_rules = apr_array_make(ctx->pool,0,sizeof(mapcache_forwarding_rule*));
1138   service->maxsize=2048;
1139   service->service.url_prefix = apr_pstrdup(ctx->pool,"");
1140   service->service.name = apr_pstrdup(ctx->pool,"wms");
1141   service->service.type = MAPCACHE_SERVICE_WMS;
1142   service->service.parse_request = _mapcache_service_wms_parse_request;
1143   service->service.create_capabilities_response = _create_capabilities_wms;
1144   service->service.configuration_parse_xml = _configuration_parse_wms_xml;
1145   service->service.format_error = _format_error_wms;
1146   service->getmap_strategy = MAPCACHE_GETMAP_ASSEMBLE;
1147   service->resample_mode = MAPCACHE_RESAMPLE_BILINEAR;
1148   service->getmap_format = NULL;
1149   service->allow_format_override = 0;
1150   return (mapcache_service*)service;
1151 }
1152 
1153 /** @} */
1154 /* vim: ts=2 sts=2 et sw=2
1155 */
1156