1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  MapCache tile caching support file: WMS datasources
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 
31 #include "mapcache.h"
32 #include "ezxml.h"
33 #include <apr_tables.h>
34 #include <apr_strings.h>
35 
36 typedef struct mapcache_source_wms mapcache_source_wms;
37 
38 /**\class mapcache_source_wms
39  * \brief WMS mapcache_source
40  * \implements mapcache_source
41  */
42 struct mapcache_source_wms {
43   mapcache_source source;
44   apr_table_t *wms_default_params; /**< default WMS parameters (SERVICE,REQUEST,STYLES,VERSION) */
45   apr_table_t *getmap_params; /**< WMS parameters specified in configuration */
46   apr_table_t *getfeatureinfo_params; /**< WMS parameters specified in configuration */
47   mapcache_http *http;
48 };
49 
50 /**
51  * \private \memberof mapcache_source_wms
52  * \sa mapcache_source::render_map()
53  */
_mapcache_source_wms_render_map(mapcache_context * ctx,mapcache_source * psource,mapcache_map * map)54 void _mapcache_source_wms_render_map(mapcache_context *ctx, mapcache_source *psource, mapcache_map *map)
55 {
56   mapcache_source_wms *wms = (mapcache_source_wms*)psource;
57   mapcache_http *http;
58   apr_table_t *params = apr_table_clone(ctx->pool,wms->wms_default_params);
59 
60   apr_table_setn(params,"BBOX",apr_psprintf(ctx->pool,"%f,%f,%f,%f",
61                  map->extent.minx,map->extent.miny,map->extent.maxx,map->extent.maxy));
62   apr_table_setn(params,"WIDTH",apr_psprintf(ctx->pool,"%d",map->width));
63   apr_table_setn(params,"HEIGHT",apr_psprintf(ctx->pool,"%d",map->height));
64   apr_table_setn(params,"FORMAT","image/png");
65   apr_table_setn(params,"SRS",map->grid_link->grid->srs);
66 
67   apr_table_overlap(params,wms->getmap_params,APR_OVERLAP_TABLES_SET);
68 
69   if(map->dimensions && map->dimensions->nelts>0) {
70     int i;
71     for(i=0; i<map->dimensions->nelts; i++) {
72       mapcache_requested_dimension *rdim = APR_ARRAY_IDX(map->dimensions,i,mapcache_requested_dimension*);
73       /* set both DIM_key=val and key=val KVP params */
74       apr_table_setn(params,rdim->dimension->name,rdim->requested_value);
75       if(strcasecmp(rdim->dimension->name,"TIME") && strcasecmp(rdim->dimension->name,"ELEVATION")) {
76         char *dim_name = apr_pstrcat(ctx->pool,"DIM_",rdim->dimension->name,NULL);
77         apr_table_setn(params,dim_name,rdim->requested_value);
78       }
79     }
80   }
81 
82   /* if the source has no LAYERS parameter defined, then use the tileset name
83    * as the LAYERS to request. When using mirror-mode, the source has no layers
84    * defined, it is added based on the incoming request
85    */
86   if(!apr_table_get(params,"layers")) {
87     apr_table_set(params,"LAYERS",map->tileset->name);
88   }
89 
90   map->encoded_data = mapcache_buffer_create(30000,ctx->pool);
91   http = mapcache_http_clone(ctx, wms->http);
92   http->url = mapcache_http_build_url(ctx,http->url,params);
93   mapcache_http_do_request(ctx,http,map->encoded_data,NULL,NULL);
94   GC_CHECK_ERROR(ctx);
95 
96   if(!mapcache_imageio_is_raw_tileset(map->tileset) && !mapcache_imageio_is_valid_format(ctx,map->encoded_data)) {
97     char *returned_data = apr_pstrndup(ctx->pool,(char*)map->encoded_data->buf,map->encoded_data->size);
98     ctx->set_error(ctx, 502, "wms request for tileset %s returned an unsupported format:\n%s",
99                    map->tileset->name, returned_data);
100   }
101 }
102 
_mapcache_source_wms_query(mapcache_context * ctx,mapcache_source * source,mapcache_feature_info * fi)103 void _mapcache_source_wms_query(mapcache_context *ctx, mapcache_source *source, mapcache_feature_info *fi)
104 {
105   mapcache_map *map = (mapcache_map*)fi;
106   mapcache_http *http;
107   mapcache_source_wms *wms = (mapcache_source_wms*)source;
108 
109   apr_table_t *params = apr_table_clone(ctx->pool,wms->wms_default_params);
110   apr_table_overlap(params,wms->getmap_params,0);
111   apr_table_setn(params,"BBOX",apr_psprintf(ctx->pool,"%f,%f,%f,%f",
112                  map->extent.minx,map->extent.miny,map->extent.maxx,map->extent.maxy));
113   apr_table_setn(params,"REQUEST","GetFeatureInfo");
114   apr_table_setn(params,"WIDTH",apr_psprintf(ctx->pool,"%d",map->width));
115   apr_table_setn(params,"HEIGHT",apr_psprintf(ctx->pool,"%d",map->height));
116   apr_table_setn(params,"SRS",map->grid_link->grid->srs);
117   apr_table_setn(params,"X",apr_psprintf(ctx->pool,"%d",fi->i));
118   apr_table_setn(params,"Y",apr_psprintf(ctx->pool,"%d",fi->j));
119   apr_table_setn(params,"INFO_FORMAT",fi->format);
120 
121   apr_table_overlap(params,wms->getfeatureinfo_params,0);
122 
123   if(map->dimensions && map->dimensions->nelts>0) {
124     int i;
125     for(i=0; i<map->dimensions->nelts; i++) {
126       mapcache_requested_dimension *rdim = APR_ARRAY_IDX(map->dimensions,i,mapcache_requested_dimension*);
127       /* set both DIM_key=val and key=val KVP params */
128       apr_table_setn(params,rdim->dimension->name,rdim->requested_value);
129       if(strcasecmp(rdim->dimension->name,"TIME") && strcasecmp(rdim->dimension->name,"ELEVATION")) {
130         char *dim_name = apr_pstrcat(ctx->pool,"DIM_",rdim->dimension->name,NULL);
131         apr_table_setn(params,dim_name,rdim->requested_value);
132       }
133     }
134   }
135 
136   fi->data = mapcache_buffer_create(30000,ctx->pool);
137   http = mapcache_http_clone(ctx, wms->http);
138   http->url = mapcache_http_build_url(ctx,http->url,params);
139   mapcache_http_do_request(ctx,http,fi->data,NULL,NULL);
140   GC_CHECK_ERROR(ctx);
141 
142 }
143 
144 /**
145  * \private \memberof mapcache_source_wms
146  * \sa mapcache_source::configuration_parse()
147  */
_mapcache_source_wms_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_source * source,mapcache_cfg * config)148 void _mapcache_source_wms_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source, mapcache_cfg *config)
149 {
150   ezxml_t cur_node;
151   mapcache_source_wms *src = (mapcache_source_wms*)source;
152 
153 
154   if ((cur_node = ezxml_child(node,"getmap")) != NULL) {
155     ezxml_t gm_node;
156     if ((gm_node = ezxml_child(cur_node,"params")) != NULL) {
157       for(gm_node = gm_node->child; gm_node; gm_node = gm_node->sibling) {
158         apr_table_set(src->getmap_params, gm_node->name, gm_node->txt);
159       }
160     } else {
161       ctx->set_error(ctx,400,"wms source %s <getmap> has no <params> block (should contain at least <LAYERS> child)",source->name);
162       return;
163     }
164   } else {
165     ctx->set_error(ctx,400,"wms source %s has no <getmap> block",source->name);
166     return;
167   }
168   if ((cur_node = ezxml_child(node,"getfeatureinfo")) != NULL) {
169     ezxml_t fi_node;
170     if ((fi_node = ezxml_child(cur_node,"info_formats")) != NULL) {
171       char *key,*last;
172       char *iformats;
173       source->info_formats = apr_array_make(ctx->pool,3,sizeof(char*));
174       iformats = apr_pstrdup(ctx->pool,fi_node->txt);
175 
176       for (key = apr_strtok(iformats, "," , &last); key != NULL;
177            key = apr_strtok(NULL, ",", &last)) {
178         APR_ARRAY_PUSH(source->info_formats,char*) = key;
179       }
180     } else {
181       ctx->set_error(ctx,400,"wms source %s <getfeatureinfo> has no <info_formats> tag",source->name);
182       return;
183     }
184     if ((fi_node = ezxml_child(cur_node,"params")) != NULL) {
185       for(fi_node = fi_node->child; fi_node; fi_node = fi_node->sibling) {
186         apr_table_set(src->getfeatureinfo_params, fi_node->name, fi_node->txt);
187       }
188     } else {
189       ctx->set_error(ctx,400,"wms source %s <getfeatureinfo> has no <params> block (should contain at least <QUERY_LAYERS> child)",source->name);
190       return;
191     }
192   }
193   if ((cur_node = ezxml_child(node,"http")) != NULL) {
194     src->http = mapcache_http_configuration_parse_xml(ctx,cur_node);
195   }
196 }
197 
198 /**
199  * \private \memberof mapcache_source_wms
200  * \sa mapcache_source::configuration_check()
201  */
_mapcache_source_wms_configuration_check(mapcache_context * ctx,mapcache_cfg * cfg,mapcache_source * source)202 void _mapcache_source_wms_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg,
203     mapcache_source *source)
204 {
205   mapcache_source_wms *src = (mapcache_source_wms*)source;
206   /* check all required parameters are configured */
207   if(!src->http) {
208     ctx->set_error(ctx, 400, "wms source %s has no <http> request configured",source->name);
209   }
210   if(!apr_table_get(src->getmap_params,"LAYERS")) {
211     if(cfg->mode == MAPCACHE_MODE_NORMAL) {
212       ctx->set_error(ctx, 400, "wms source %s has no LAYERS", source->name);
213     }
214   }
215   if(source->info_formats) {
216     if(!apr_table_get(src->getfeatureinfo_params,"QUERY_LAYERS")) {
217       ctx->set_error(ctx, 400, "wms source %s has no QUERY_LAYERS", source->name);
218     }
219   }
220 }
221 
mapcache_source_wms_create(mapcache_context * ctx)222 mapcache_source* mapcache_source_wms_create(mapcache_context *ctx)
223 {
224   mapcache_source_wms *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_wms));
225   if(!source) {
226     ctx->set_error(ctx, 500, "failed to allocate wms source");
227     return NULL;
228   }
229   mapcache_source_init(ctx, &(source->source));
230   source->source.type = MAPCACHE_SOURCE_WMS;
231   source->source._render_map = _mapcache_source_wms_render_map;
232   source->source.configuration_check = _mapcache_source_wms_configuration_check;
233   source->source.configuration_parse_xml = _mapcache_source_wms_configuration_parse_xml;
234   source->source._query_info = _mapcache_source_wms_query;
235   source->wms_default_params = apr_table_make(ctx->pool,4);;
236   source->getmap_params = apr_table_make(ctx->pool,4);
237   source->getfeatureinfo_params = apr_table_make(ctx->pool,4);
238   apr_table_add(source->wms_default_params,"VERSION","1.1.1");
239   apr_table_add(source->wms_default_params,"REQUEST","GetMap");
240   apr_table_add(source->wms_default_params,"SERVICE","WMS");
241   apr_table_add(source->wms_default_params,"STYLES","");
242   return (mapcache_source*)source;
243 }
244 
245 
246 
247 /* vim: ts=2 sts=2 et sw=2
248 */
249