1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  MapCache tile caching support file: OGC dimensions
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 <sys/types.h>
34 #ifdef USE_PCRE
35 #include <pcre.h>
36 #else
37 #include <regex.h>
38 #endif
39 
40 typedef struct mapcache_dimension_values mapcache_dimension_values;
41 typedef struct mapcache_dimension_regex mapcache_dimension_regex;
42 typedef struct mapcache_dimension_composite mapcache_dimension_composite;
43 
44 struct mapcache_dimension_values {
45   mapcache_dimension dimension;
46   apr_array_header_t *values;
47   int case_sensitive;
48 };
49 
50 
51 struct mapcache_dimension_regex {
52   mapcache_dimension dimension;
53   char *regex_string;
54 #ifdef USE_PCRE
55   pcre *pcregex;
56 #else
57   regex_t *regex;
58 #endif
59 };
60 
61 
62 
mapcache_requested_dimensions_clone(apr_pool_t * pool,apr_array_header_t * src)63 apr_array_header_t *mapcache_requested_dimensions_clone(apr_pool_t *pool, apr_array_header_t *src) {
64   apr_array_header_t *ret = NULL;
65   if(src) {
66     int i;
67     ret = apr_array_make(pool,src->nelts,sizeof(mapcache_requested_dimension*));
68     for(i=0; i<src->nelts; i++) {
69       mapcache_requested_dimension *tiledim = apr_pcalloc(pool,sizeof(mapcache_requested_dimension));
70       mapcache_requested_dimension *srcdim = APR_ARRAY_IDX(src,i,mapcache_requested_dimension*);
71       *tiledim = *srcdim;
72       APR_ARRAY_PUSH(ret,mapcache_requested_dimension*) = tiledim;
73     }
74   }
75   return ret;
76 }
77 
mapcache_set_requested_dimension(mapcache_context * ctx,apr_array_header_t * dimensions,const char * name,const char * value)78 void mapcache_set_requested_dimension(mapcache_context *ctx, apr_array_header_t *dimensions, const char *name, const char *value) {
79   int i;
80   if(!dimensions || dimensions->nelts <= 0) {
81     ctx->set_error(ctx,500,"BUG: no dimensions configure for tile/map");
82     return;
83   }
84   for(i=0;i<dimensions->nelts;i++) {
85     mapcache_requested_dimension *dim = APR_ARRAY_IDX(dimensions,i,mapcache_requested_dimension*);
86     if(!strcasecmp(dim->dimension->name,name)) {
87       dim->requested_value = value?apr_pstrdup(ctx->pool,value):NULL;
88       return;
89     }
90   }
91   ctx->set_error(ctx,500,"BUG: dimension (%s) not found in tile/map",name);
92 }
93 
mapcache_set_cached_dimension(mapcache_context * ctx,apr_array_header_t * dimensions,const char * name,const char * value)94 void mapcache_set_cached_dimension(mapcache_context *ctx, apr_array_header_t *dimensions, const char *name, const char *value) {
95   int i;
96   if(!dimensions || dimensions->nelts <= 0) {
97     ctx->set_error(ctx,500,"BUG: no dimensions configure for tile/map");
98     return;
99   }
100   for(i=0;i<dimensions->nelts;i++) {
101     mapcache_requested_dimension *dim = APR_ARRAY_IDX(dimensions,i,mapcache_requested_dimension*);
102     if(!strcasecmp(dim->dimension->name,name)) {
103       dim->cached_value = value?apr_pstrdup(ctx->pool,value):NULL;
104       return;
105     }
106   }
107   ctx->set_error(ctx,500,"BUG: dimension (%s) not found in tile/map",name);
108 }
109 
mapcache_tile_set_cached_dimension(mapcache_context * ctx,mapcache_tile * tile,const char * name,const char * value)110 void mapcache_tile_set_cached_dimension(mapcache_context *ctx, mapcache_tile *tile, const char *name, const char *value) {
111   mapcache_set_cached_dimension(ctx,tile->dimensions,name,value);
112 }
113 
mapcache_map_set_cached_dimension(mapcache_context * ctx,mapcache_map * map,const char * name,const char * value)114 void mapcache_map_set_cached_dimension(mapcache_context *ctx, mapcache_map *map, const char *name, const char *value) {
115   mapcache_set_cached_dimension(ctx,map->dimensions,name,value);
116 }
mapcache_tile_set_requested_dimension(mapcache_context * ctx,mapcache_tile * tile,const char * name,const char * value)117 void mapcache_tile_set_requested_dimension(mapcache_context *ctx, mapcache_tile *tile, const char *name, const char *value) {
118   mapcache_set_requested_dimension(ctx,tile->dimensions,name,value);
119 }
120 
mapcache_map_set_requested_dimension(mapcache_context * ctx,mapcache_map * map,const char * name,const char * value)121 void mapcache_map_set_requested_dimension(mapcache_context *ctx, mapcache_map *map, const char *name, const char *value) {
122   mapcache_set_requested_dimension(ctx,map->dimensions,name,value);
123 }
124 
_mapcache_dimension_regex_get_entries_for_value(mapcache_context * ctx,mapcache_dimension * dim,const char * value,mapcache_tileset * tileset,mapcache_extent * extent,mapcache_grid * grid)125 static apr_array_header_t* _mapcache_dimension_regex_get_entries_for_value(mapcache_context *ctx, mapcache_dimension *dim, const char *value,
126                        mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
127 {
128   mapcache_dimension_regex *dimension = (mapcache_dimension_regex*)dim;
129   apr_array_header_t *values = apr_array_make(ctx->pool,1,sizeof(char*));
130 #ifdef USE_PCRE
131   int ovector[30];
132   int rc = pcre_exec(dimension->pcregex,NULL,value,strlen(value),0,0,ovector,30);
133   if(rc>0) {
134     APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,value);
135   }
136 #else
137   if(!regexec(dimension->regex,value,0,0,0)) {
138     APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,value);
139   }
140 #endif
141   else {
142     ctx->set_error(ctx,400,"failed to validate requested value for %s (%s)",dim->class_name,dim->name);
143   }
144   return values;
145 }
146 
_mapcache_dimension_regex_get_all_entries(mapcache_context * ctx,mapcache_dimension * dim,mapcache_tileset * tileset,mapcache_extent * extent,mapcache_grid * grid)147 static apr_array_header_t* _mapcache_dimension_regex_get_all_entries(mapcache_context *ctx, mapcache_dimension *dim,
148                        mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
149 {
150   mapcache_dimension_regex *dimension = (mapcache_dimension_regex*)dim;
151   apr_array_header_t *ret = apr_array_make(ctx->pool,1,sizeof(char*));
152   APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,dimension->regex_string);
153   return ret;
154 }
155 
156 
_mapcache_dimension_regex_parse_xml(mapcache_context * ctx,mapcache_dimension * dim,ezxml_t node)157 static void _mapcache_dimension_regex_parse_xml(mapcache_context *ctx, mapcache_dimension *dim,
158     ezxml_t node)
159 {
160   mapcache_dimension_regex *dimension;
161   ezxml_t child_node = ezxml_child(node,"regex");
162 
163   dimension = (mapcache_dimension_regex*)dim;
164 
165   if(child_node && child_node->txt && *child_node->txt) {
166     dimension->regex_string = apr_pstrdup(ctx->pool,child_node->txt);
167   } else {
168     ctx->set_error(ctx,400,"failed to parse %s regex: no <regex> child supplied",dim->class_name);
169     return;
170   }
171 #ifdef USE_PCRE
172   {
173     const char *pcre_err;
174     int pcre_offset;
175     dimension->pcregex = pcre_compile(dimension->regex_string,0,&pcre_err, &pcre_offset,0);
176     if(!dimension->pcregex) {
177       ctx->set_error(ctx,400,"failed to compile regular expression \"%s\" for %s \"%s\": %s",
178                      dimension->regex_string,dim->class_name,dim->name,pcre_err);
179       return;
180     }
181   }
182 #else
183   {
184     int rc = regcomp(dimension->regex, dimension->regex_string, REG_EXTENDED);
185     if(rc) {
186       char errmsg[200];
187       regerror(rc,dimension->regex,errmsg,200);
188       ctx->set_error(ctx,400,"failed to compile regular expression \"%s\" for %s \"%s\": %s",
189                      dimension->regex_string,dim->class_name,dim->name,errmsg);
190       return;
191     }
192   }
193 #endif
194 
195 }
196 
_mapcache_dimension_values_get_entries_for_value(mapcache_context * ctx,mapcache_dimension * dim,const char * value,mapcache_tileset * tileset,mapcache_extent * extent,mapcache_grid * grid)197 static apr_array_header_t* _mapcache_dimension_values_get_entries_for_value(mapcache_context *ctx, mapcache_dimension *dim, const char *value,
198                        mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
199 {
200   int i;
201   mapcache_dimension_values *dimension = (mapcache_dimension_values*)dim;
202   apr_array_header_t *values = apr_array_make(ctx->pool,1,sizeof(char*));
203   for(i=0; i<dimension->values->nelts; i++) {
204     char *cur_val = APR_ARRAY_IDX(dimension->values,i,char*);
205     if(dimension->case_sensitive) {
206       if(!strcmp(value,cur_val)) {
207         APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,value);
208         break;
209       }
210     } else {
211       if(!strcasecmp(value,cur_val)) {
212         APR_ARRAY_PUSH(values,char*) = apr_pstrdup(ctx->pool,value);
213         break;
214       }
215     }
216   }
217   if(i == dimension->values->nelts) {
218     ctx->set_error(ctx,400,"failed to validate requested value for %s (%s)",dim->class_name,dim->name);
219   }
220   return values;
221 }
222 
_mapcache_dimension_values_get_all_entries(mapcache_context * ctx,mapcache_dimension * dim,mapcache_tileset * tileset,mapcache_extent * extent,mapcache_grid * grid)223 static apr_array_header_t* _mapcache_dimension_values_get_all_entries(mapcache_context *ctx, mapcache_dimension *dim,
224                        mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid)
225 {
226   mapcache_dimension_values *dimension = (mapcache_dimension_values*)dim;
227   apr_array_header_t *ret = apr_array_make(ctx->pool,dimension->values->nelts,sizeof(char*));
228   int i;
229   for(i=0; i<dimension->values->nelts; i++) {
230     APR_ARRAY_PUSH(ret,char*) = apr_pstrdup(ctx->pool,APR_ARRAY_IDX(dimension->values,i,char*));
231   }
232   return ret;
233 }
234 
235 
_mapcache_dimension_values_parse_xml(mapcache_context * ctx,mapcache_dimension * dim,ezxml_t node)236 static void _mapcache_dimension_values_parse_xml(mapcache_context *ctx, mapcache_dimension *dim,
237     ezxml_t node)
238 {
239   mapcache_dimension_values *dimension;
240   ezxml_t child_node = ezxml_child(node,"value");
241   dimension = (mapcache_dimension_values*)dim;
242 
243   if(!child_node) {
244     ctx->set_error(ctx,400,"failed to parse %s values: no <value> children supplied",dim->class_name);
245     return;
246   }
247   for(; child_node; child_node = child_node->next) {
248     const char* entry = child_node->txt;
249     if(!entry || !*entry) {
250       ctx->set_error(ctx,400,"failed to parse %s values: empty <value>",dim->class_name);
251       return;
252     }
253     APR_ARRAY_PUSH(dimension->values,char*) = apr_pstrdup(ctx->pool,entry);
254   }
255 
256   child_node = ezxml_child(node,"case_sensitive");
257   if(child_node && child_node->txt) {
258     if(!strcasecmp(child_node->txt,"true")) {
259       dimension->case_sensitive = 1;
260     }
261   }
262 
263   if(!dimension->values->nelts) {
264     ctx->set_error(ctx, 400, "<dimension> \"%s\" has no values",dim->name);
265     return;
266   }
267 }
268 
269 
mapcache_dimension_get_entries_for_value(mapcache_context * ctx,mapcache_dimension * dimension,const char * value,mapcache_tileset * tileset,mapcache_extent * extent,mapcache_grid * grid)270 apr_array_header_t* mapcache_dimension_get_entries_for_value(mapcache_context *ctx, mapcache_dimension *dimension, const char *value,
271                        mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid) {
272   if(!dimension->isTime) {
273     return dimension->_get_entries_for_value(ctx, dimension, value, tileset, extent, grid);
274   } else {
275     return mapcache_dimension_time_get_entries_for_value(ctx, dimension, value, tileset, extent, grid);
276   }
277 }
278 
mapcache_dimension_values_create(mapcache_context * ctx,apr_pool_t * pool)279 mapcache_dimension* mapcache_dimension_values_create(mapcache_context *ctx, apr_pool_t *pool)
280 {
281   mapcache_dimension_values *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_values));
282   dimension->dimension.type = MAPCACHE_DIMENSION_VALUES;
283   dimension->dimension.class_name = "dimension";
284   dimension->values = apr_array_make(pool,1,sizeof(char*));
285   dimension->dimension._get_entries_for_value = _mapcache_dimension_values_get_entries_for_value;
286   dimension->dimension.configuration_parse_xml = _mapcache_dimension_values_parse_xml;
287   dimension->dimension.get_all_entries = _mapcache_dimension_values_get_all_entries;
288   dimension->dimension.get_all_ogc_formatted_entries = _mapcache_dimension_values_get_all_entries;
289   return (mapcache_dimension*)dimension;
290 }
291 
mapcache_dimension_regex_create(mapcache_context * ctx,apr_pool_t * pool)292 mapcache_dimension* mapcache_dimension_regex_create(mapcache_context *ctx, apr_pool_t *pool)
293 {
294   mapcache_dimension_regex *dimension = apr_pcalloc(pool, sizeof(mapcache_dimension_regex));
295   dimension->dimension.type = MAPCACHE_DIMENSION_REGEX;
296   dimension->dimension.class_name = "dimension";
297 #ifndef USE_PCRE
298   dimension->regex = (regex_t*)apr_pcalloc(pool, sizeof(regex_t));
299 #endif
300   dimension->dimension._get_entries_for_value = _mapcache_dimension_regex_get_entries_for_value;
301   dimension->dimension.configuration_parse_xml = _mapcache_dimension_regex_parse_xml;
302   dimension->dimension.get_all_entries = _mapcache_dimension_regex_get_all_entries;
303   dimension->dimension.get_all_ogc_formatted_entries = _mapcache_dimension_regex_get_all_entries;
304   return (mapcache_dimension*)dimension;
305 }
306 
307 
308 
309 /* vim: ts=2 sts=2 et sw=2
310 */
311