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