1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: MapCache tile caching support file: xml configuration parser
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 "ezxml.h"
32 #include <string.h>
33 #include <stdlib.h>
34 #include <apr_strings.h>
35 #include <apr_hash.h>
36 #include <apr_tables.h>
37 #include <apr_file_io.h>
38 #include <apr_file_info.h>
39 #include <math.h>
40
41
parseMetadata(mapcache_context * ctx,ezxml_t node,apr_table_t * metadata)42 void parseMetadata(mapcache_context *ctx, ezxml_t node, apr_table_t *metadata)
43 {
44 ezxml_t cur_node;
45 for(cur_node = node->child; cur_node; cur_node = cur_node->ordered) {
46 if (!cur_node->child) {
47 // Parse simple text
48 apr_table_add(metadata, cur_node->name, cur_node->txt);
49 } else {
50 // Parse tags:
51 // `>` suffix in name indicates that value is a table and not a string
52 char * name = apr_pstrcat(ctx->pool,cur_node->name,">",NULL);
53 apr_table_t * contents = apr_table_make(ctx->pool,3);
54 ezxml_t sub_node;
55 for(sub_node = cur_node->child; sub_node; sub_node = sub_node->ordered) {
56 apr_table_add(contents, sub_node->name, sub_node->txt);
57 }
58 apr_table_addn(metadata, name, (const char *)contents);
59 }
60 }
61 }
62
parseDimensions(mapcache_context * ctx,ezxml_t node,mapcache_tileset * tileset)63 void parseDimensions(mapcache_context *ctx, ezxml_t node, mapcache_tileset *tileset)
64 {
65 ezxml_t dimension_node;
66 ezxml_t wms_querybymap_node;
67 apr_array_header_t *dimensions = apr_array_make(ctx->pool,1,sizeof(mapcache_dimension*));
68 for(dimension_node = ezxml_child(node,"dimension"); dimension_node; dimension_node = dimension_node->next) {
69 char *name = (char*)ezxml_attr(dimension_node,"name");
70 char *type = (char*)ezxml_attr(dimension_node,"type");
71 char *unit = (char*)ezxml_attr(dimension_node,"unit");
72 char *time = (char*)ezxml_attr(dimension_node,"time");
73 char *default_value = (char*)ezxml_attr(dimension_node,"default");
74
75 mapcache_dimension *dimension = NULL;
76
77 if(!name || !strlen(name)) {
78 ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in <dimension>");
79 return;
80 }
81
82 if(type && *type) {
83 if(!strcmp(type,"values")) {
84 dimension = mapcache_dimension_values_create(ctx,ctx->pool);
85 } else if(!strcmp(type,"regex")) {
86 dimension = mapcache_dimension_regex_create(ctx,ctx->pool);
87 } else if(!strcmp(type,"postgresql")) {
88 dimension = mapcache_dimension_postgresql_create(ctx,ctx->pool);
89 } else if(!strcmp(type,"sqlite")) {
90 dimension = mapcache_dimension_sqlite_create(ctx,ctx->pool);
91 } else if(!strcmp(type,"elasticsearch")) {
92 dimension = mapcache_dimension_elasticsearch_create(ctx,ctx->pool);
93 } else if(!strcmp(type,"time")) {
94 //backwards compatibility
95 dimension = mapcache_dimension_sqlite_create(ctx,ctx->pool);
96 dimension->isTime = 1;
97 } else {
98 ctx->set_error(ctx,400,"unknown dimension type \"%s\"",type);
99 return;
100 }
101 } else {
102 ctx->set_error(ctx,400, "mandatory attribute \"type\" not found in <dimensions>");
103 return;
104 }
105 GC_CHECK_ERROR(ctx);
106
107 dimension->name = apr_pstrdup(ctx->pool,name);
108
109 if(unit && *unit) {
110 dimension->unit = apr_pstrdup(ctx->pool,unit);
111 }
112
113 if(time && *time && !strcasecmp(time,"true")) {
114 dimension->isTime = 1;
115 }
116
117 if(default_value && *default_value) {
118 dimension->default_value = apr_pstrdup(ctx->pool,default_value);
119 } else {
120 ctx->set_error(ctx,400,"dimension \"%s\" has no \"default\" attribute",dimension->name);
121 return;
122 }
123
124 dimension->wms_querybymap_minzoom = -1;
125 wms_querybymap_node = ezxml_child(dimension_node,"wms_querybymap");
126 if (wms_querybymap_node && wms_querybymap_node->txt) {
127 if (!strcasecmp(wms_querybymap_node->txt,"true")) {
128 const char * minzoom = ezxml_attr(wms_querybymap_node,"minzoom");
129 dimension->wms_querybymap_minzoom = 0;
130 if (minzoom && *minzoom) {
131 char *endptr;
132 dimension->wms_querybymap_minzoom = strtol(minzoom,&endptr,10);
133 if (*endptr != 0 || dimension->wms_querybymap_minzoom < 0) {
134 ctx->set_error(ctx, 400, "failed to parse minzoom \"%s\" for <wms_querybymap>"
135 "expecting an integer starting from 0",minzoom);
136 return;
137 }
138 }
139 } else if (strcasecmp(wms_querybymap_node->txt,"false")) {
140 ctx->set_error(ctx,400,"failed to parse <wms_querybymap> (%s), expecting \"true\" or \"false\"",wms_querybymap_node->txt);
141 return;
142 }
143 }
144
145 dimension->configuration_parse_xml(ctx,dimension,dimension_node);
146 GC_CHECK_ERROR(ctx);
147
148 APR_ARRAY_PUSH(dimensions,mapcache_dimension*) = dimension;
149 }
150 if(apr_is_empty_array(dimensions)) {
151 ctx->set_error(ctx, 400, "<dimensions> for tileset \"%s\" has no dimensions defined (expecting <dimension> children)",tileset->name);
152 return;
153 }
154
155 tileset->dimensions = dimensions;
156 dimension_node = ezxml_child(node,"store_assemblies");
157 if(dimension_node && dimension_node->txt) {
158 if(!strcmp(dimension_node->txt,"false")) {
159 tileset->store_dimension_assemblies = 0;
160 } else if(strcmp(dimension_node->txt,"true")) {
161 ctx->set_error(ctx,400,"failed to parse <store_assemblies> (%s), expecting \"true\" or \"false\"",dimension_node->txt);
162 return;
163 }
164 }
165
166 dimension_node = ezxml_child(node,"assembly_type");
167 if(dimension_node) {
168 if(!strcmp(dimension_node->txt,"stack")) {
169 tileset->dimension_assembly_type = MAPCACHE_DIMENSION_ASSEMBLY_STACK;
170 } else if(!strcmp(dimension_node->txt,"animate")) {
171 tileset->dimension_assembly_type = MAPCACHE_DIMENSION_ASSEMBLY_ANIMATE;
172 ctx->set_error(ctx,400,"animate dimension assembly mode not implemented");
173 return;
174 } else if(strcmp(dimension_node->txt,"none")) {
175 ctx->set_error(ctx,400,"unknown dimension assembly mode (%s). Can be one of \"stack\" or \"none\"",dimension_node->txt);
176 return;
177 } else {
178 tileset->dimension_assembly_type = MAPCACHE_DIMENSION_ASSEMBLY_NONE;
179 }
180 }
181
182 tileset->assembly_threaded_fetching_maxzoom = -1;
183 dimension_node = ezxml_child(node,"assembly_threaded_fetching");
184 if (dimension_node) {
185 if (dimension_node && dimension_node->txt) {
186 if (!strcmp(dimension_node->txt,"true")) {
187 int maxzoom = INT_MAX;
188 char * smaxzoom = (char*)ezxml_attr(dimension_node,"maxzoom");;
189 if (smaxzoom && *smaxzoom) {
190 char *endptr;
191 maxzoom = (int)strtol(smaxzoom,&endptr,10);
192 if(*endptr != 0 || maxzoom < 0) {
193 ctx->set_error(ctx, 400, "failed to parse assembly_threaded_fetching"
194 " maxzoom %s (expecting a positive integer)", smaxzoom);
195 return;
196 }
197 }
198 tileset->assembly_threaded_fetching_maxzoom = maxzoom;
199 } else if (strcmp(dimension_node->txt,"false")) {
200 ctx->set_error(ctx,400,"failed to parse <assembly_threaded_fetching>"
201 " (%s), expecting \"true\" or \"false\"",dimension_node->txt);
202 return;
203 }
204 }
205 }
206
207 /* should we create subdimensions from source if not found in cache.
208 e.g. if dimension=mosaic returns dimension=val1,val2,val3 should we
209 query the wms source with dimension=val1 , dimension=val2 and/or
210 dimension=val3 if they are not found in cache */
211 dimension_node = ezxml_child(node,"subdimensions_read_only");
212 if(dimension_node) {
213 if(tileset->dimension_assembly_type == MAPCACHE_DIMENSION_ASSEMBLY_NONE) {
214 ctx->set_error(ctx,400,"<subdimensions_read_only> used on a tileset with no <assembly_type> set, which makes no sense");
215 return;
216 }
217 if(dimension_node && dimension_node->txt && !strcmp(dimension_node->txt,"true")) {
218 tileset->subdimension_read_only = 1;
219 } else if(strcmp(dimension_node->txt,"false")) {
220 ctx->set_error(ctx,400,"failed to parse <subdimensions_read_only> (%s), expecting \"true\" or \"false\"",dimension_node->txt);
221 return;
222 }
223 }
224 }
225
parseRuleset(mapcache_context * ctx,ezxml_t node,mapcache_cfg * config)226 void parseRuleset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
227 {
228 char *name;
229 mapcache_ruleset *ruleset;
230 ezxml_t cur_node;
231 int i;
232
233 name = (char*)ezxml_attr(node,"name");
234
235 if(!name || !strlen(name)) {
236 ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in <ruleset>");
237 return;
238 } else {
239 name = apr_pstrdup(ctx->pool, name);
240 /* check we don't already have a ruleset defined with this name */
241 if(mapcache_configuration_get_ruleset(config, name)) {
242 ctx->set_error(ctx, 400, "duplicate ruleset with name \"%s\"",name);
243 return;
244 }
245 }
246
247 ruleset = mapcache_ruleset_create(ctx->pool);
248 ruleset->name = name;
249
250 /* parse rules, <rule> */
251 for(cur_node = ezxml_child(node,"rule"), i = 0; cur_node; cur_node = cur_node->next, i++) {
252 int *zoom, nzoom, j;
253 char* zoom_attr = (char*)ezxml_attr(cur_node, "zoom_level");
254 ezxml_t visibility = ezxml_child(cur_node, "visibility");
255 mapcache_rule *rule = mapcache_ruleset_rule_create(ctx->pool);
256
257 /* parse zoom_level attribute */
258 if(zoom_attr && *zoom_attr) {
259 char *value = apr_pstrdup(ctx->pool, zoom_attr);
260 if(MAPCACHE_SUCCESS != mapcache_util_extract_int_list(ctx, value, NULL, &zoom, &nzoom) || nzoom < 1) {
261 ctx->set_error(ctx, 400, "failed to parse zoom_level array %s in ruleset %s, rule %d. "
262 "(expecting space separated integers, eg <rule zoom_level=\"0 1 2\">)",
263 value, ruleset->name, i+1);
264 return;
265 }
266 } else {
267 ctx->set_error(ctx, 400, "zoom_level not set in rule %d", i+1);
268 return;
269 }
270
271 /* parse visibility, <visibility> */
272 if(visibility) {
273 char *hidden_color = (char*)ezxml_attr(visibility, "hidden_color");
274 ezxml_t extent_node;
275
276 if (hidden_color && *hidden_color) {
277 /* parse color, base 16 */
278 rule->hidden_color = (unsigned int)strtol(hidden_color, NULL, 16);
279
280 if (strlen(hidden_color) <= 6) {
281 /* if color is set, but no alpha value. Assume no transparency. */
282 rule->hidden_color += 0xff000000;
283 }
284 }
285
286 /* parse extents, <extent> */
287 for (extent_node = ezxml_child(visibility,"extent"); extent_node; extent_node = extent_node->next) {
288 double *values;
289 int nvalues;
290 char *value = apr_pstrdup(ctx->pool,extent_node->txt);
291 mapcache_extent extent = {0,0,0,0};
292 mapcache_extent *pextent;
293
294 if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, value, NULL, &values, &nvalues) ||
295 nvalues != 4) {
296 ctx->set_error(ctx, 400, "failed to parse extent array %s in ruleset %s, rule %d. "
297 "(expecting 4 space separated numbers, got %d (%f %f %f %f)"
298 "eg <extent>-180 -90 180 90</extent>)",
299 value,ruleset->name,i+1,nvalues,values[0],values[1],values[2],values[3]);
300 return;
301 }
302
303 extent.minx = values[0];
304 extent.miny = values[1];
305 extent.maxx = values[2];
306 extent.maxy = values[3];
307 pextent = (mapcache_extent*)apr_pcalloc(ctx->pool, sizeof(mapcache_extent));
308 *pextent = extent;
309 APR_ARRAY_PUSH(rule->visible_extents, mapcache_extent*) = pextent;
310 }
311 }
312
313 /* add this rule for given zoom_levels */
314 for(j = 0; j < nzoom; j++) {
315 mapcache_rule *clone_rule = mapcache_ruleset_rule_clone(ctx->pool, rule);
316 /* check for duplicate rule for this zoom level */
317 if(mapcache_ruleset_rule_find(ruleset->rules, zoom[j]) != NULL) {
318 ctx->set_error(ctx, 400, "found duplicate rule for zoom_level %d", zoom[j]);
319 return;
320 }
321 clone_rule->zoom_level = zoom[j];
322 APR_ARRAY_PUSH(ruleset->rules, mapcache_rule*) = clone_rule;
323 }
324 }
325 mapcache_configuration_add_ruleset(config,ruleset,ruleset->name);
326 }
327
parseGrid(mapcache_context * ctx,ezxml_t node,mapcache_cfg * config)328 void parseGrid(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
329 {
330 char *name;
331 mapcache_extent extent = {0,0,0,0};
332 mapcache_grid *grid;
333 ezxml_t cur_node;
334 char *value;
335
336 name = (char*)ezxml_attr(node,"name");
337 if(!name || !strlen(name)) {
338 ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in <grid>");
339 return;
340 } else {
341 name = apr_pstrdup(ctx->pool, name);
342 /* check we don't already have a grid defined with this name */
343 if(mapcache_configuration_get_grid(config, name)) {
344 ctx->set_error(ctx, 400, "duplicate grid with name \"%s\"",name);
345 return;
346 }
347 }
348 grid = mapcache_grid_create(ctx->pool);
349 grid->name = name;
350
351 if ((cur_node = ezxml_child(node,"extent")) != NULL) {
352 double *values;
353 int nvalues;
354 value = apr_pstrdup(ctx->pool,cur_node->txt);
355 if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, value, NULL, &values, &nvalues) ||
356 nvalues != 4) {
357 ctx->set_error(ctx, 400, "failed to parse extent array %s."
358 "(expecting 4 space separated numbers, got %d (%f %f %f %f)"
359 "eg <extent>-180 -90 180 90</extent>",
360 value,nvalues,values[0],values[1],values[2],values[3]);
361 return;
362 }
363 extent.minx = values[0];
364 extent.miny = values[1];
365 extent.maxx = values[2];
366 extent.maxy = values[3];
367 }
368
369 if ((cur_node = ezxml_child(node,"metadata")) != NULL) {
370 parseMetadata(ctx, cur_node, grid->metadata);
371 GC_CHECK_ERROR(ctx);
372 }
373
374 if ((cur_node = ezxml_child(node,"units")) != NULL) {
375 if(!strcasecmp(cur_node->txt,"dd")) {
376 grid->unit = MAPCACHE_UNIT_DEGREES;
377 } else if(!strcasecmp(cur_node->txt,"m")) {
378 grid->unit = MAPCACHE_UNIT_METERS;
379 } else if(!strcasecmp(cur_node->txt,"ft")) {
380 grid->unit = MAPCACHE_UNIT_FEET;
381 } else {
382 ctx->set_error(ctx, 400, "unknown unit %s for grid %s (valid values are \"dd\", \"m\", and \"ft\"",
383 cur_node->txt, grid->name);
384 return;
385 }
386 }
387 if ((cur_node = ezxml_child(node,"srs")) != NULL) {
388 grid->srs = apr_pstrdup(ctx->pool,cur_node->txt);
389 }
390
391 for(cur_node = ezxml_child(node,"srsalias"); cur_node; cur_node = cur_node->next) {
392 value = apr_pstrdup(ctx->pool,cur_node->txt);
393 APR_ARRAY_PUSH(grid->srs_aliases,char*) = value;
394 }
395
396 if ((cur_node = ezxml_child(node,"origin")) != NULL) {
397 if(!strcasecmp(cur_node->txt,"top-left")) {
398 grid->origin = MAPCACHE_GRID_ORIGIN_TOP_LEFT;
399 } else if(!strcasecmp(cur_node->txt,"bottom-left")) {
400 grid->origin = MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT;
401 } else if(!strcasecmp(cur_node->txt,"top-right")) {
402 grid->origin = MAPCACHE_GRID_ORIGIN_TOP_RIGHT;
403 } else if(!strcasecmp(cur_node->txt,"bottom-right")) {
404 grid->origin = MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT;
405 } else {
406 ctx->set_error(ctx, 400,
407 "unknown origin %s for grid %s (valid values are \"top-left\", \"bottom-left\", \"top-right\" and \"bottom-right\"",
408 cur_node->txt, grid->name);
409 return;
410 }
411 if(grid->origin == MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT || grid->origin == MAPCACHE_GRID_ORIGIN_TOP_RIGHT) {
412 ctx->set_error(ctx,500,"grid origin %s not implemented",cur_node->txt);
413 return;
414 }
415 }
416 if ((cur_node = ezxml_child(node,"size")) != NULL) {
417 int *sizes, nsizes;
418 value = apr_pstrdup(ctx->pool,cur_node->txt);
419
420 if(MAPCACHE_SUCCESS != mapcache_util_extract_int_list(ctx, value, NULL, &sizes, &nsizes) ||
421 nsizes != 2) {
422 ctx->set_error(ctx, 400, "failed to parse size array %s in grid %s"
423 "(expecting two space separated integers, eg <size>256 256</size>",
424 value, grid->name);
425 return;
426 }
427 grid->tile_sx = sizes[0];
428 grid->tile_sy = sizes[1];
429 }
430
431 if ((cur_node = ezxml_child(node,"resolutions")) != NULL) {
432 int nvalues;
433 double *values;
434 value = apr_pstrdup(ctx->pool,cur_node->txt);
435
436 if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, value, NULL, &values, &nvalues) ||
437 !nvalues) {
438 ctx->set_error(ctx, 400, "failed to parse resolutions array %s."
439 "(expecting space separated numbers, "
440 "eg <resolutions>1 2 4 8 16 32</resolutions>",
441 value);
442 return;
443 }
444 grid->nlevels = nvalues;
445 grid->levels = (mapcache_grid_level**)apr_pcalloc(ctx->pool,
446 grid->nlevels*sizeof(mapcache_grid_level));
447 while(nvalues--) {
448 double unitheight;
449 double unitwidth;
450 mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(ctx->pool,sizeof(mapcache_grid_level));
451 level->resolution = values[nvalues];
452 unitheight = grid->tile_sy * level->resolution;
453 unitwidth = grid->tile_sx * level->resolution;
454 level->maxy = ceil((extent.maxy-extent.miny - 0.01* unitheight)/unitheight);
455 level->maxx = ceil((extent.maxx-extent.minx - 0.01* unitwidth)/unitwidth);
456 grid->levels[nvalues] = level;
457 }
458 }
459
460 if(grid->srs == NULL) {
461 ctx->set_error(ctx, 400, "grid \"%s\" has no srs configured."
462 " You must add a <srs> tag.", grid->name);
463 return;
464 }
465 if(extent.minx >= extent.maxx || extent.miny >= extent.maxy) {
466 ctx->set_error(ctx, 400, "grid \"%s\" has no (or invalid) extent configured"
467 " You must add/correct a <extent> tag.", grid->name);
468 return;
469 } else {
470 grid->extent = extent;
471 }
472 if(grid->tile_sx <= 0 || grid->tile_sy <= 0) {
473 ctx->set_error(ctx, 400, "grid \"%s\" has no (or invalid) tile size configured"
474 " You must add/correct a <size> tag.", grid->name);
475 return;
476 }
477 if(!grid->nlevels) {
478 ctx->set_error(ctx, 400, "grid \"%s\" has no resolutions configured."
479 " You must add a <resolutions> tag.", grid->name);
480 return;
481 }
482 mapcache_configuration_add_grid(config,grid,name);
483 }
484
parseSource(mapcache_context * ctx,ezxml_t node,mapcache_cfg * config)485 void parseSource(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
486 {
487 ezxml_t cur_node;
488 char *name = NULL, *type = NULL;
489 mapcache_source *source;
490
491 name = (char*)ezxml_attr(node,"name");
492 type = (char*)ezxml_attr(node,"type");
493 if(!name || !strlen(name)) {
494 ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in <source>");
495 return;
496 } else {
497 name = apr_pstrdup(ctx->pool, name);
498 /* check we don't already have a source defined with this name */
499 if(mapcache_configuration_get_source(config, name)) {
500 ctx->set_error(ctx, 400, "duplicate source with name \"%s\"",name);
501 return;
502 }
503 }
504 if(!type || !strlen(type)) {
505 ctx->set_error(ctx, 400, "mandatory attribute \"type\" not found in <source>");
506 return;
507 }
508 source = NULL;
509 if(!strcmp(type,"wms")) {
510 source = mapcache_source_wms_create(ctx);
511 } else if(!strcmp(type,"mapserver")) {
512 source = mapcache_source_mapserver_create(ctx);
513 } else if(!strcmp(type,"gdal")) {
514 source = mapcache_source_gdal_create(ctx);
515 } else if(!strcmp(type,"dummy")) {
516 source = mapcache_source_dummy_create(ctx);
517 } else if(!strcmp(type,"fallback")) {
518 source = mapcache_source_fallback_create(ctx);
519 } else {
520 ctx->set_error(ctx, 400, "unknown source type %s for source \"%s\"", type, name);
521 return;
522 }
523 if(source == NULL) {
524 ctx->set_error(ctx, 400, "failed to parse source \"%s\"", name);
525 return;
526 }
527 source->name = name;
528
529 if ((cur_node = ezxml_child(node,"metadata")) != NULL) {
530 parseMetadata(ctx, cur_node, source->metadata);
531 GC_CHECK_ERROR(ctx);
532 }
533 if ((cur_node = ezxml_child(node,"retries")) != NULL) {
534 source->retry_count = atoi(cur_node->txt);
535 if(source->retry_count > 10) {
536 ctx->set_error(ctx,400,"source (%s) <retries> count of %d is unreasonably large. max is 10", source->name, source->retry_count);
537 return;
538 }
539 }
540 if ((cur_node = ezxml_child(node,"retry_delay")) != NULL) {
541 source->retry_delay = (double)atof(cur_node->txt);
542 if(source->retry_delay < 0) {
543 ctx->set_error(ctx,400,"source (%s) retry delay of %f must be positive",source->name, source->retry_delay);
544 return;
545 }
546 }
547
548 source->configuration_parse_xml(ctx,node,source, config);
549 GC_CHECK_ERROR(ctx);
550 source->configuration_check(ctx,config,source);
551 GC_CHECK_ERROR(ctx);
552 mapcache_configuration_add_source(config,source,name);
553 }
554
parseFormat(mapcache_context * ctx,ezxml_t node,mapcache_cfg * config)555 void parseFormat(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
556 {
557 char *name = NULL, *type = NULL;
558 mapcache_image_format *format = NULL;
559 ezxml_t cur_node;
560 name = (char*)ezxml_attr(node,"name");
561 type = (char*)ezxml_attr(node,"type");
562 if(!name || !strlen(name)) {
563 ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in <format>");
564 return;
565 }
566 name = apr_pstrdup(ctx->pool, name);
567 if(!type || !strlen(type)) {
568 ctx->set_error(ctx, 400, "mandatory attribute \"type\" not found in <format>");
569 return;
570 }
571 if(!strcmp(type,"PNG")) {
572 int colors = -1;
573 mapcache_compression_type compression = MAPCACHE_COMPRESSION_DEFAULT;
574 if ((cur_node = ezxml_child(node,"compression")) != NULL) {
575 if(!strcmp(cur_node->txt, "fast")) {
576 compression = MAPCACHE_COMPRESSION_FAST;
577 } else if(!strcmp(cur_node->txt, "best")) {
578 compression = MAPCACHE_COMPRESSION_BEST;
579 } else if(!strcmp(cur_node->txt, "none")) {
580 compression = MAPCACHE_COMPRESSION_DISABLE;
581 } else {
582 ctx->set_error(ctx, 400, "unknown compression type %s for format \"%s\"", cur_node->txt, name);
583 return;
584 }
585 }
586 if ((cur_node = ezxml_child(node,"colors")) != NULL) {
587 char *endptr;
588 colors = (int)strtol(cur_node->txt,&endptr,10);
589 if(*endptr != 0 || colors < 2 || colors > 256) {
590 ctx->set_error(ctx, 400, "failed to parse colors \"%s\" for format \"%s\""
591 "(expecting an integer between 2 and 256 "
592 "eg <colors>256</colors>",
593 cur_node->txt,name);
594 return;
595 }
596 }
597
598 if(colors == -1) {
599 format = mapcache_imageio_create_png_format(ctx->pool,
600 name,compression);
601 } else {
602 format = mapcache_imageio_create_png_q_format(ctx->pool,
603 name,compression, colors);
604 }
605 } else if(!strcmp(type,"JPEG")) {
606 int quality = 95;
607 int optimize = TRUE;
608 mapcache_photometric photometric = MAPCACHE_PHOTOMETRIC_YCBCR;
609 if ((cur_node = ezxml_child(node,"quality")) != NULL) {
610 char *endptr;
611 quality = (int)strtol(cur_node->txt,&endptr,10);
612 if(*endptr != 0 || quality < 1 || quality > 100) {
613 ctx->set_error(ctx, 400, "failed to parse quality \"%s\" for format \"%s\""
614 "(expecting an integer between 1 and 100 "
615 "eg <quality>90</quality>",
616 cur_node->txt,name);
617 return;
618 }
619 }
620 if ((cur_node = ezxml_child(node,"photometric")) != NULL) {
621 if(!strcasecmp(cur_node->txt,"RGB"))
622 photometric = MAPCACHE_PHOTOMETRIC_RGB;
623 else if(!strcasecmp(cur_node->txt,"YCBCR"))
624 photometric = MAPCACHE_PHOTOMETRIC_YCBCR;
625 else {
626 ctx->set_error(ctx,500,"failed to parse jpeg format %s photometric %s. expecting rgb or ycbcr",
627 name,cur_node->txt);
628 return;
629 }
630 }
631 if ((cur_node = ezxml_child(node,"optimize")) != NULL) {
632 if(cur_node->txt && !strcasecmp(cur_node->txt,"false"))
633 optimize = MAPCACHE_OPTIMIZE_NO;
634 else if(cur_node->txt && !strcasecmp(cur_node->txt,"true"))
635 optimize = MAPCACHE_OPTIMIZE_YES;
636 else if(cur_node->txt && !strcasecmp(cur_node->txt,"arithmetic"))
637 optimize = MAPCACHE_OPTIMIZE_ARITHMETIC;
638 else {
639 ctx->set_error(ctx,500,"failed to parse jpeg format %s optimize %s. expecting true, false or arithmetic",
640 name,cur_node->txt);
641 return;
642 }
643 }
644 format = mapcache_imageio_create_jpeg_format(ctx->pool,
645 name,quality,photometric,optimize);
646 } else if(!strcasecmp(type,"MIXED")) {
647 mapcache_image_format *transparent=NULL, *opaque=NULL;
648 unsigned int alpha_cutoff=255;
649 if ((cur_node = ezxml_child(node,"transparent")) != NULL) {
650 transparent = mapcache_configuration_get_image_format(config,cur_node->txt);
651 }
652 if(!transparent) {
653 ctx->set_error(ctx,400, "mixed format %s references unknown transparent format %s"
654 "(order is important, format %s should appear first)",
655 name,cur_node->txt,cur_node->txt);
656 return;
657 }
658 if ((cur_node = ezxml_child(node,"opaque")) != NULL) {
659 opaque = mapcache_configuration_get_image_format(config,cur_node->txt);
660 }
661 if(!opaque) {
662 ctx->set_error(ctx,400, "mixed format %s references unknown opaque format %s"
663 "(order is important, format %s should appear first)",
664 name,cur_node->txt,cur_node->txt);
665 return;
666 }
667 if ((cur_node = ezxml_child(node,"alpha_cutoff")) != NULL) {
668 alpha_cutoff = atoi(cur_node->txt);
669 }
670 format = mapcache_imageio_create_mixed_format(ctx->pool,name,transparent, opaque, alpha_cutoff);
671 } else if(!strcasecmp(type,"RAW")) {
672 char *extension=NULL;
673 char *mime_type=NULL;
674 if ((cur_node = ezxml_child(node,"extension")) != NULL) extension = apr_pstrdup(ctx->pool, cur_node->txt);
675 if ((cur_node = ezxml_child(node,"mime_type")) != NULL) mime_type = apr_pstrdup(ctx->pool, cur_node->txt);
676 format = mapcache_imageio_create_raw_format(ctx->pool,name,extension,mime_type);
677 } else {
678 ctx->set_error(ctx, 400, "unknown format type %s for format \"%s\"", type, name);
679 return;
680 }
681 if(format == NULL) {
682 ctx->set_error(ctx, 400, "failed to parse format \"%s\"", name);
683 return;
684 }
685
686 mapcache_configuration_add_image_format(config,format,name);
687 return;
688 }
689
parseCache(mapcache_context * ctx,ezxml_t node,mapcache_cfg * config)690 void parseCache(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
691 {
692 char *name = NULL, *type = NULL;
693 mapcache_cache *cache = NULL;
694 ezxml_t cur_node;
695 name = (char*)ezxml_attr(node,"name");
696 type = (char*)ezxml_attr(node,"type");
697 if(!name || !strlen(name)) {
698 ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in <cache>");
699 return;
700 } else {
701 name = apr_pstrdup(ctx->pool, name);
702 /* check we don't already have a cache defined with this name */
703 if(mapcache_configuration_get_cache(config, name)) {
704 ctx->set_error(ctx, 400, "duplicate cache with name \"%s\"",name);
705 return;
706 }
707 }
708 if(!type || !strlen(type)) {
709 ctx->set_error(ctx, 400, "mandatory attribute \"type\" not found in <cache>");
710 return;
711 }
712 if(!strcmp(type,"disk")) {
713 cache = mapcache_cache_disk_create(ctx);
714 } else if(!strcmp(type,"fallback")) {
715 cache = mapcache_cache_fallback_create(ctx);
716 } else if(!strcmp(type,"multitier")) {
717 cache = mapcache_cache_multitier_create(ctx);
718 } else if(!strcmp(type,"composite")) {
719 cache = mapcache_cache_composite_create(ctx);
720 } else if(!strcmp(type,"rest")) {
721 cache = mapcache_cache_rest_create(ctx);
722 } else if(!strcmp(type,"s3")) {
723 cache = mapcache_cache_s3_create(ctx);
724 } else if(!strcmp(type,"azure")) {
725 cache = mapcache_cache_azure_create(ctx);
726 } else if(!strcmp(type,"google")) {
727 cache = mapcache_cache_google_create(ctx);
728 } else if(!strcmp(type,"bdb")) {
729 cache = mapcache_cache_bdb_create(ctx);
730 } else if(!strcmp(type,"tokyocabinet")) {
731 cache = mapcache_cache_tc_create(ctx);
732 } else if(!strcmp(type,"sqlite3")) {
733 cache = mapcache_cache_sqlite_create(ctx);
734 } else if(!strcmp(type,"mbtiles")) {
735 cache = mapcache_cache_mbtiles_create(ctx);
736 } else if(!strcmp(type,"memcache")) {
737 cache = mapcache_cache_memcache_create(ctx);
738 } else if(!strcmp(type,"tiff")) {
739 cache = mapcache_cache_tiff_create(ctx);
740 } else if(!strcmp(type,"couchbase")) {
741 cache = mapcache_cache_couchbase_create(ctx);
742 } else if(!strcmp(type,"riak")) {
743 cache = mapcache_cache_riak_create(ctx);
744 } else {
745 ctx->set_error(ctx, 400, "unknown cache type %s for cache \"%s\"", type, name);
746 return;
747 }
748 GC_CHECK_ERROR(ctx);
749 if(cache == NULL) {
750 ctx->set_error(ctx, 400, "failed to parse cache \"%s\"", name);
751 return;
752 }
753 cache->name = name;
754
755 if ((cur_node = ezxml_child(node,"retries")) != NULL) {
756 cache->retry_count = atoi(cur_node->txt);
757 if(cache->retry_count > 10) {
758 ctx->set_error(ctx,400,"cache (%s) <retries> count of %d is unreasonably large. max is 10", cache->name, cache->retry_count);
759 return;
760 }
761 }
762 if ((cur_node = ezxml_child(node,"retry_delay")) != NULL) {
763 cache->retry_delay = (double)atof(cur_node->txt);
764 if(cache->retry_delay < 0) {
765 ctx->set_error(ctx,400,"cache (%s) retry delay of %f must be positive",cache->name, cache->retry_delay);
766 return;
767 }
768 }
769
770
771 cache->configuration_parse_xml(ctx,node,cache,config);
772 GC_CHECK_ERROR(ctx);
773 mapcache_configuration_add_cache(config,cache,name);
774 return;
775 }
776
parseTileset(mapcache_context * ctx,ezxml_t node,mapcache_cfg * config)777 void parseTileset(mapcache_context *ctx, ezxml_t node, mapcache_cfg *config)
778 {
779 char *name = NULL;
780 mapcache_tileset *tileset = NULL;
781 ezxml_t cur_node;
782 char* value;
783 int havewgs84bbox=0;
784
785 if(config->mode == MAPCACHE_MODE_NORMAL) {
786 name = (char*)ezxml_attr(node,"name");
787 } else {
788 name = "mirror";
789 }
790 if(!name || !strlen(name)) {
791 ctx->set_error(ctx, 400, "mandatory attribute \"name\" not found in <tileset>");
792 return;
793 } else {
794 name = apr_pstrdup(ctx->pool, name);
795 /* check we don't already have a cache defined with this name */
796 if(mapcache_configuration_get_tileset(config, name)) {
797 ctx->set_error(ctx, 400, "duplicate tileset with name \"%s\"",name);
798 return;
799 }
800 }
801 tileset = mapcache_tileset_create(ctx);
802 tileset->name = name;
803
804 if ((cur_node = ezxml_child(node,"read-only")) != NULL) {
805 if(cur_node->txt && !strcmp(cur_node->txt,"true"))
806 tileset->read_only = 1;
807 }
808
809 if ((cur_node = ezxml_child(node,"metadata")) != NULL) {
810 parseMetadata(ctx, cur_node, tileset->metadata);
811 GC_CHECK_ERROR(ctx);
812 }
813
814
815 if ((value = (char*)apr_table_get(tileset->metadata,"wgs84boundingbox")) != NULL) {
816 double *values;
817 int nvalues;
818 value = apr_pstrdup(ctx->pool,value);
819 if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, value, NULL, &values, &nvalues) ||
820 nvalues != 4) {
821 ctx->set_error(ctx, 400, "failed to parse extent array %s."
822 "(expecting 4 space separated numbers, got %d (%f %f %f %f)"
823 "eg <wgs84bbox>-180 -90 180 90</wgs84bbox>",
824 value,nvalues,values[0],values[1],values[2],values[3]);
825 return;
826 }
827 tileset->wgs84bbox.minx = values[0];
828 tileset->wgs84bbox.miny = values[1];
829 tileset->wgs84bbox.maxx = values[2];
830 tileset->wgs84bbox.maxy = values[3];
831 havewgs84bbox = 1;
832 }
833
834 if ((cur_node = ezxml_child(node,"format")) != NULL) {
835 mapcache_image_format *format = mapcache_configuration_get_image_format(config,cur_node->txt);
836 if(!format) {
837 ctx->set_error(ctx, 400, "tileset \"%s\" references format \"%s\","
838 " but it is not configured",name,cur_node->txt);
839 return;
840 }
841 tileset->format = format;
842 }
843
844 for(cur_node = ezxml_child(node,"grid"); cur_node; cur_node = cur_node->next) {
845 mapcache_grid *grid;
846 mapcache_grid_link *gridlink;
847 mapcache_ruleset *ruleset = NULL;
848 char *restrictedExtent = NULL, *sTolerance = NULL, *ruleset_name = NULL;
849 mapcache_extent *extent;
850 int tolerance;
851 int i;
852
853 if (tileset->grid_links == NULL) {
854 tileset->grid_links = apr_array_make(ctx->pool,1,sizeof(mapcache_grid_link*));
855 }
856 grid = mapcache_configuration_get_grid(config, cur_node->txt);
857 if(!grid) {
858 ctx->set_error(ctx, 400, "tileset \"%s\" references grid \"%s\","
859 " but it is not configured", name, cur_node->txt);
860 return;
861 }
862 gridlink = apr_pcalloc(ctx->pool,sizeof(mapcache_grid_link));
863 gridlink->grid = grid;
864 gridlink->minz = 0;
865 gridlink->maxz = grid->nlevels;
866 gridlink->grid_limits = (mapcache_extent_i*)apr_pcalloc(ctx->pool,grid->nlevels*sizeof(mapcache_extent_i));
867 gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_NOTCONFIGURED;
868 gridlink->intermediate_grids = apr_array_make(ctx->pool,1,sizeof(mapcache_grid_link*));
869 gridlink->rules = apr_array_make(ctx->pool,0,sizeof(mapcache_rule*));
870
871 ruleset_name = (char*)ezxml_attr(cur_node,"ruleset");
872 if(ruleset_name) {
873 ruleset = mapcache_configuration_get_ruleset(config, ruleset_name);
874 if(!ruleset) {
875 ctx->set_error(ctx, 400, "grid \"%s\" in tileset \"%s\" references ruleset \"%s\","
876 " but it is not configured", cur_node->txt, name, ruleset_name);
877 return;
878 }
879 }
880
881 restrictedExtent = (char*)ezxml_attr(cur_node,"restricted_extent");
882 if(restrictedExtent) {
883 int nvalues;
884 double *values;
885 restrictedExtent = apr_pstrdup(ctx->pool,restrictedExtent);
886 if(MAPCACHE_SUCCESS != mapcache_util_extract_double_list(ctx, restrictedExtent, NULL, &values, &nvalues) ||
887 nvalues != 4) {
888 ctx->set_error(ctx, 400, "failed to parse extent array %s."
889 "(expecting 4 space separated numbers, "
890 "eg <grid restricted_extent=\"-180 -90 180 90\">foo</grid>",
891 restrictedExtent);
892 return;
893 }
894 gridlink->restricted_extent = (mapcache_extent*) apr_pcalloc(ctx->pool, sizeof(mapcache_extent));
895 gridlink->restricted_extent->minx = values[0];
896 gridlink->restricted_extent->miny = values[1];
897 gridlink->restricted_extent->maxx = values[2];
898 gridlink->restricted_extent->maxy = values[3];
899 extent = gridlink->restricted_extent;
900 } else {
901 extent = &grid->extent;
902 }
903
904 tolerance = 5;
905 sTolerance = (char*)ezxml_attr(cur_node,"tolerance");
906 if(sTolerance) {
907 char *endptr;
908 tolerance = (int)strtol(sTolerance,&endptr,10);
909 if(*endptr != 0 || tolerance < 0) {
910 ctx->set_error(ctx, 400, "failed to parse grid tolerance %s (expecting a positive integer)",
911 sTolerance);
912 return;
913 }
914 }
915 sTolerance = (char*)ezxml_attr(cur_node,"use_wms_intermediate_resolutions");
916 if(sTolerance && !strcmp(sTolerance,"true")) {
917 mapcache_grid_link *intermediate_gridlink = apr_pcalloc(ctx->pool,sizeof(mapcache_grid_link));
918 APR_ARRAY_PUSH(gridlink->intermediate_grids,mapcache_grid_link*) = intermediate_gridlink;
919 }
920
921 mapcache_grid_compute_limits(grid,extent,gridlink->grid_limits,tolerance);
922
923 // setup zoom level rules if configured
924 if(ruleset) {
925 mapcache_buffer *last_hidden_tile = NULL;
926 unsigned int last_hidden_color;
927
928 // prepare one rule per zoom level. if rule is missing it will be NULL
929 for(i = 0; i < grid->nlevels; i++) {
930 mapcache_rule *rule = mapcache_ruleset_rule_find(ruleset->rules, i);
931
932 if(rule) {
933 mapcache_rule *rule_clone = mapcache_ruleset_rule_clone(ctx->pool, rule);
934
935 if(rule->visible_extents) {
936 int j;
937
938 // create blank tile in configured format to return when outside visible extent
939 // try to reuse last tile if possible
940 if(last_hidden_tile == NULL || last_hidden_color != rule->hidden_color) {
941 if(tileset->format) {
942 last_hidden_tile = tileset->format->create_empty_image(ctx, tileset->format, grid->tile_sx, grid->tile_sy, rule->hidden_color);
943 } else {
944 last_hidden_tile = config->default_image_format->create_empty_image(ctx, config->default_image_format, grid->tile_sx, grid->tile_sy, rule->hidden_color);
945 }
946
947 if(GC_HAS_ERROR(ctx)) {
948 return;
949 }
950
951 last_hidden_color = rule->hidden_color;
952 }
953
954 rule_clone->hidden_tile = last_hidden_tile;
955
956 // compute limits for extents
957 for(j = 0; j < rule->visible_extents->nelts; j++) {
958 mapcache_extent *visible_extent = APR_ARRAY_IDX(rule->visible_extents, j, mapcache_extent*);
959 mapcache_extent_i *visible_limit = apr_pcalloc(ctx->pool, sizeof(mapcache_extent_i));
960 mapcache_grid_compute_limits_at_level(grid,visible_extent,visible_limit,tolerance,i);
961 APR_ARRAY_PUSH(rule_clone->visible_limits, mapcache_extent_i*) = visible_limit;
962 }
963 }
964 APR_ARRAY_PUSH(gridlink->rules, mapcache_rule*) = rule_clone;
965 } else {
966 // no rule for zoom level
967 APR_ARRAY_PUSH(gridlink->rules, mapcache_rule*) = NULL;
968 }
969 }
970 }
971
972 sTolerance = (char*)ezxml_attr(cur_node,"minzoom");
973 if(sTolerance) {
974 char *endptr;
975 tolerance = (int)strtol(sTolerance,&endptr,10);
976 if(*endptr != 0 || tolerance < 0) {
977 ctx->set_error(ctx, 400, "failed to parse grid minzoom %s (expecting a positive integer)",
978 sTolerance);
979 return;
980 }
981 gridlink->minz = tolerance;
982 }
983
984 sTolerance = (char*)ezxml_attr(cur_node,"maxzoom");
985 if(sTolerance) {
986 char *endptr;
987 tolerance = (int)strtol(sTolerance,&endptr,10);
988 if(*endptr != 0 || tolerance < 0) {
989 ctx->set_error(ctx, 400, "failed to parse grid maxzoom %s (expecting a positive integer)",
990 sTolerance);
991 return;
992 }
993 gridlink->maxz = tolerance + 1;
994 }
995
996 if(gridlink->minz<0 || gridlink->maxz>grid->nlevels || gridlink->minz>=gridlink->maxz) {
997 ctx->set_error(ctx, 400, "invalid grid maxzoom/minzoom %d/%d", gridlink->minz,gridlink->maxz);
998 return;
999 }
1000
1001 sTolerance = (char*)ezxml_attr(cur_node,"max-cached-zoom");
1002 /* RFC97 implementation: check for a maximum zoomlevel to cache */
1003 if(sTolerance) {
1004 char *endptr;
1005 tolerance = (int)strtol(sTolerance,&endptr,10);
1006 if(*endptr != 0 || tolerance < 0) {
1007 ctx->set_error(ctx, 400, "failed to parse grid max-cached-zoom %s (expecting a positive integer)",
1008 sTolerance);
1009 return;
1010 }
1011
1012 if(tolerance > gridlink->maxz) {
1013 ctx->set_error(ctx, 400, "failed to parse grid max-cached-zoom %s (max cached zoom is greater than grid's max zoom)",
1014 sTolerance);
1015 return;
1016 }
1017 gridlink->max_cached_zoom = tolerance;
1018
1019 /* default to reassembling */
1020 gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_REASSEMBLE;
1021 sTolerance = (char*)ezxml_attr(cur_node,"out-of-zoom-strategy");
1022 if(sTolerance) {
1023 if(!strcasecmp(sTolerance,"reassemble")) {
1024 gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_REASSEMBLE;
1025 }
1026 else if(!strcasecmp(sTolerance,"proxy")) {
1027 gridlink->outofzoom_strategy = MAPCACHE_OUTOFZOOM_PROXY;
1028 } else {
1029 ctx->set_error(ctx, 400, "failed to parse grid out-of-zoom-strategy %s (expecting \"reassemble\" or \"proxy\")",
1030 sTolerance);
1031 return;
1032 }
1033 }
1034 }
1035
1036 /* compute wgs84 bbox if it wasn't supplied already */
1037 if(!havewgs84bbox && !strcasecmp(grid->srs,"EPSG:4326")) {
1038 tileset->wgs84bbox = *extent;
1039 }
1040
1041 if(gridlink->intermediate_grids->nelts > 0) {
1042 double factor = 0.5, unitheight,unitwidth;
1043 int i;
1044 mapcache_grid_link *igl = APR_ARRAY_IDX(gridlink->intermediate_grids, 0, mapcache_grid_link*);
1045 igl->restricted_extent = gridlink->restricted_extent;
1046 igl->minz = gridlink->minz;
1047 igl->max_cached_zoom = gridlink->max_cached_zoom - 1;
1048 igl->maxz = gridlink->maxz - 1;
1049 igl->outofzoom_strategy = gridlink->outofzoom_strategy;
1050 igl->grid = mapcache_grid_create(ctx->pool);
1051 igl->grid->extent = gridlink->grid->extent;
1052 igl->grid->name = apr_psprintf(ctx->pool,"%s_intermediate_%g",gridlink->grid->name,factor);
1053 igl->grid->nlevels = gridlink->grid->nlevels - 1;
1054 igl->grid->origin = gridlink->grid->origin;
1055 igl->grid->srs = gridlink->grid->srs;
1056 igl->grid->srs_aliases = gridlink->grid->srs_aliases;
1057 igl->grid->unit = gridlink->grid->unit;
1058 igl->grid->tile_sx = gridlink->grid->tile_sx + gridlink->grid->tile_sx * factor;
1059 igl->grid->tile_sy = gridlink->grid->tile_sy + gridlink->grid->tile_sy * factor;
1060 igl->grid->levels = (mapcache_grid_level**)apr_pcalloc(ctx->pool, igl->grid->nlevels*sizeof(mapcache_grid_level*));
1061 for(i=0; i<igl->grid->nlevels; i++) {
1062 mapcache_grid_level *level = (mapcache_grid_level*)apr_pcalloc(ctx->pool,sizeof(mapcache_grid_level));
1063 level->resolution = gridlink->grid->levels[i]->resolution + (gridlink->grid->levels[i+1]->resolution - gridlink->grid->levels[i]->resolution) * factor;
1064 unitheight = igl->grid->tile_sy * level->resolution;
1065 unitwidth = igl->grid->tile_sx * level->resolution;
1066
1067 level->maxy = ceil((igl->grid->extent.maxy-igl->grid->extent.miny - 0.01* unitheight)/unitheight);
1068 level->maxx = ceil((igl->grid->extent.maxx-igl->grid->extent.minx - 0.01* unitwidth)/unitwidth);
1069 igl->grid->levels[i] = level;
1070 }
1071 igl->grid_limits = (mapcache_extent_i*)apr_pcalloc(ctx->pool,igl->grid->nlevels*sizeof(mapcache_extent_i));
1072 mapcache_grid_compute_limits(igl->grid,extent,igl->grid_limits,tolerance);
1073 }
1074 APR_ARRAY_PUSH(tileset->grid_links,mapcache_grid_link*) = gridlink;
1075 }
1076
1077 if ((cur_node = ezxml_child(node,"dimensions")) != NULL) {
1078 parseDimensions(ctx, cur_node, tileset);
1079 GC_CHECK_ERROR(ctx);
1080 }
1081
1082 if ((cur_node = ezxml_child(node,"cache")) != NULL) {
1083 mapcache_cache *cache = mapcache_configuration_get_cache(config, cur_node->txt);
1084 if(!cache) {
1085 ctx->set_error(ctx, 400, "tileset \"%s\" references cache \"%s\","
1086 " but it is not configured", name, cur_node->txt);
1087 return;
1088 }
1089 tileset->_cache = cache;
1090 }
1091
1092 if ((cur_node = ezxml_child(node,"source")) != NULL) {
1093 mapcache_source *source = mapcache_configuration_get_source(config, cur_node->txt);
1094 if(!source) {
1095 ctx->set_error(ctx, 400, "tileset \"%s\" references source \"%s\","
1096 " but it is not configured", name, cur_node->txt);
1097 return;
1098 }
1099 tileset->source = source;
1100 }
1101
1102 if ((cur_node = ezxml_child(node,"metatile")) != NULL) {
1103 int *values, nvalues;
1104 value = apr_pstrdup(ctx->pool,cur_node->txt);
1105
1106 if(MAPCACHE_SUCCESS != mapcache_util_extract_int_list(ctx, cur_node->txt, NULL,
1107 &values, &nvalues) ||
1108 nvalues != 2) {
1109 ctx->set_error(ctx, 400, "failed to parse metatile dimension %s."
1110 "(expecting 2 space separated integers, "
1111 "eg <metatile>5 5</metatile>",
1112 cur_node->txt);
1113 return;
1114 }
1115 tileset->metasize_x = values[0];
1116 tileset->metasize_y = values[1];
1117 }
1118
1119 if ((cur_node = ezxml_child(node,"watermark")) != NULL) {
1120 if(!*cur_node->txt) {
1121 ctx->set_error(ctx,400, "watermark config entry empty");
1122 return;
1123 }
1124 mapcache_tileset_add_watermark(ctx,tileset,cur_node->txt);
1125 GC_CHECK_ERROR(ctx);
1126 }
1127
1128
1129 if ((cur_node = ezxml_child(node,"expires")) != NULL) {
1130 char *endptr;
1131 tileset->expires = (int)strtol(cur_node->txt,&endptr,10);
1132 if(*endptr != 0) {
1133 ctx->set_error(ctx, 400, "failed to parse expires %s."
1134 "(expecting an integer, "
1135 "eg <expires>3600</expires>",
1136 cur_node->txt);
1137 return;
1138 }
1139 }
1140 if ((cur_node = ezxml_child(node,"auto_expire")) != NULL) {
1141 char *endptr;
1142 tileset->auto_expire = (int)strtol(cur_node->txt,&endptr,10);
1143 if(*endptr != 0) {
1144 ctx->set_error(ctx, 400, "failed to parse auto_expire %s."
1145 "(expecting an integer, "
1146 "eg <auto_expire>3600</auto_expire>",
1147 cur_node->txt);
1148 return;
1149 }
1150 }
1151
1152 if ((cur_node = ezxml_child(node,"metabuffer")) != NULL) {
1153 char *endptr;
1154 tileset->metabuffer = (int)strtol(cur_node->txt,&endptr,10);
1155 if(*endptr != 0) {
1156 ctx->set_error(ctx, 400, "failed to parse metabuffer %s."
1157 "(expecting an integer, "
1158 "eg <metabuffer>1</metabuffer>",
1159 cur_node->txt);
1160 return;
1161 }
1162 }
1163
1164 mapcache_tileset_configuration_check(ctx,tileset);
1165 GC_CHECK_ERROR(ctx);
1166 mapcache_configuration_add_tileset(config,tileset,name);
1167 return;
1168 }
1169
parseServices(mapcache_context * ctx,ezxml_t root,mapcache_cfg * config)1170 void parseServices(mapcache_context *ctx, ezxml_t root, mapcache_cfg *config)
1171 {
1172 ezxml_t node;
1173 if ((node = ezxml_child(root,"wms")) != NULL) {
1174 if(!node->txt || !*node->txt || strcmp(node->txt, "false")) {
1175 config->services[MAPCACHE_SERVICE_WMS] = mapcache_service_wms_create(ctx);
1176 }
1177 }
1178 if ((node = ezxml_child(root,"wmts")) != NULL) {
1179 if(!node->txt || !*node->txt || strcmp(node->txt, "false")) {
1180 config->services[MAPCACHE_SERVICE_WMTS] = mapcache_service_wmts_create(ctx);
1181 }
1182 }
1183 if ((node = ezxml_child(root,"ve")) != NULL) {
1184 if(!node->txt || !*node->txt || strcmp(node->txt, "false")) {
1185 config->services[MAPCACHE_SERVICE_VE] = mapcache_service_ve_create(ctx);
1186 }
1187 }
1188 if ((node = ezxml_child(root,"tms")) != NULL) {
1189 if(!node->txt || !*node->txt || strcmp(node->txt, "false")) {
1190 config->services[MAPCACHE_SERVICE_TMS] = mapcache_service_tms_create(ctx);
1191 }
1192 }
1193 if ((node = ezxml_child(root,"kml")) != NULL) {
1194 if(!node->txt || !*node->txt || strcmp(node->txt, "false")) {
1195 if(!config->services[MAPCACHE_SERVICE_TMS]) {
1196 ctx->set_error(ctx,400,"kml service requires the tms service to be active");
1197 return;
1198 }
1199 config->services[MAPCACHE_SERVICE_KML] = mapcache_service_kml_create(ctx);
1200 }
1201 }
1202
1203 if ((node = ezxml_child(root,"gmaps")) != NULL) {
1204 if(!node->txt || !*node->txt || strcmp(node->txt, "false")) {
1205 config->services[MAPCACHE_SERVICE_GMAPS] = mapcache_service_gmaps_create(ctx);
1206 }
1207 }
1208 if ((node = ezxml_child(root,"demo")) != NULL) {
1209 if(!node->txt || !*node->txt || strcmp(node->txt, "false")) {
1210 config->services[MAPCACHE_SERVICE_DEMO] = mapcache_service_demo_create(ctx);
1211 if(!config->services[MAPCACHE_SERVICE_WMS])
1212 config->services[MAPCACHE_SERVICE_WMS] = mapcache_service_wms_create(ctx);
1213 }
1214 }
1215
1216 if(!config->services[MAPCACHE_SERVICE_WMS] &&
1217 !config->services[MAPCACHE_SERVICE_TMS] &&
1218 !config->services[MAPCACHE_SERVICE_WMTS]) {
1219 ctx->set_error(ctx, 400, "no services configured."
1220 " You must add a <services> tag with <wmts/> <wms/> or <tms/> children");
1221 return;
1222 }
1223 }
1224
1225
1226
mapcache_configuration_parse_xml(mapcache_context * ctx,const char * filename,mapcache_cfg * config)1227 void mapcache_configuration_parse_xml(mapcache_context *ctx, const char *filename, mapcache_cfg *config)
1228 {
1229 ezxml_t doc, node;
1230 const char *mode;
1231 doc = ezxml_parse_file(filename);
1232 if (doc == NULL) {
1233 ctx->set_error(ctx,400, "failed to parse file %s. Is it valid XML?", filename);
1234 goto cleanup;
1235 } else {
1236 const char *err = ezxml_error(doc);
1237 if(err && *err) {
1238 ctx->set_error(ctx,400, "failed to parse file %s: %s", filename, err);
1239 goto cleanup;
1240 }
1241 }
1242
1243 if(strcmp(doc->name,"mapcache")) {
1244 ctx->set_error(ctx,400, "failed to parse file %s. first node is not <mapcache>", filename);
1245 goto cleanup;
1246 }
1247 mode = ezxml_attr(doc,"mode");
1248 if(mode) {
1249 if(!strcmp(mode,"combined_mirror")) {
1250 config->mode = MAPCACHE_MODE_MIRROR_COMBINED;
1251 } else if(!strcmp(mode,"split_mirror")) {
1252 config->mode = MAPCACHE_MODE_MIRROR_SPLIT;
1253 } else if(!strcmp(mode,"normal")) {
1254 config->mode = MAPCACHE_MODE_NORMAL;
1255 } else {
1256 ctx->set_error(ctx,400,"unknown mode \"%s\" for <mapcache>",mode);
1257 goto cleanup;
1258 }
1259 } else {
1260 config->mode = MAPCACHE_MODE_NORMAL;
1261 }
1262
1263 for(node = ezxml_child(doc,"metadata"); node; node = node->next) {
1264 parseMetadata(ctx, node, config->metadata);
1265 if(GC_HAS_ERROR(ctx)) goto cleanup;
1266 }
1267
1268 for(node = ezxml_child(doc,"source"); node; node = node->next) {
1269 parseSource(ctx, node, config);
1270 if(GC_HAS_ERROR(ctx)) goto cleanup;
1271 }
1272
1273 for(node = ezxml_child(doc,"grid"); node; node = node->next) {
1274 parseGrid(ctx, node, config);
1275 if(GC_HAS_ERROR(ctx)) goto cleanup;
1276 }
1277
1278 for(node = ezxml_child(doc,"format"); node; node = node->next) {
1279 parseFormat(ctx, node, config);
1280 if(GC_HAS_ERROR(ctx)) goto cleanup;
1281 }
1282
1283 for(node = ezxml_child(doc,"cache"); node; node = node->next) {
1284 parseCache(ctx, node, config);
1285 if(GC_HAS_ERROR(ctx)) goto cleanup;
1286 }
1287
1288 for(node = ezxml_child(doc,"ruleset"); node; node = node->next) {
1289 parseRuleset(ctx, node, config);
1290 if(GC_HAS_ERROR(ctx)) goto cleanup;
1291 }
1292
1293 for(node = ezxml_child(doc,"tileset"); node; node = node->next) {
1294 parseTileset(ctx, node, config);
1295 if(GC_HAS_ERROR(ctx)) goto cleanup;
1296 }
1297
1298 if ((node = ezxml_child(doc,"service")) != NULL) {
1299 ezxml_t service_node;
1300 for(service_node = node; service_node; service_node = service_node->next) {
1301 char *enabled = (char*)ezxml_attr(service_node,"enabled");
1302 char *type = (char*)ezxml_attr(service_node,"type");
1303 if(!strcasecmp(enabled,"true")) {
1304 if (!strcasecmp(type,"wms")) {
1305 mapcache_service *new_service = mapcache_service_wms_create(ctx);
1306 if(new_service->configuration_parse_xml) {
1307 new_service->configuration_parse_xml(ctx,service_node,new_service,config);
1308 }
1309 config->services[MAPCACHE_SERVICE_WMS] = new_service;
1310 } else if (!strcasecmp(type,"tms")) {
1311 mapcache_service *new_service = mapcache_service_tms_create(ctx);
1312 if(new_service->configuration_parse_xml) {
1313 new_service->configuration_parse_xml(ctx,service_node,new_service,config);
1314 }
1315 config->services[MAPCACHE_SERVICE_TMS] = new_service;
1316 } else if (!strcasecmp(type,"wmts")) {
1317 mapcache_service *new_service = mapcache_service_wmts_create(ctx);
1318 if(new_service->configuration_parse_xml) {
1319 new_service->configuration_parse_xml(ctx,service_node,new_service,config);
1320 }
1321 config->services[MAPCACHE_SERVICE_WMTS] = new_service;
1322 } else if (!strcasecmp(type,"kml")) {
1323 mapcache_service *new_service = mapcache_service_kml_create(ctx);
1324 if(new_service->configuration_parse_xml) {
1325 new_service->configuration_parse_xml(ctx,service_node,new_service,config);
1326 }
1327 config->services[MAPCACHE_SERVICE_KML] = new_service;
1328 } else if (!strcasecmp(type,"gmaps")) {
1329 mapcache_service *new_service = mapcache_service_gmaps_create(ctx);
1330 if(new_service->configuration_parse_xml) {
1331 new_service->configuration_parse_xml(ctx,service_node,new_service,config);
1332 }
1333 config->services[MAPCACHE_SERVICE_GMAPS] = new_service;
1334 } else if (!strcasecmp(type,"mapguide")) {
1335 mapcache_service *new_service = mapcache_service_mapguide_create(ctx);
1336 if(new_service->configuration_parse_xml) {
1337 new_service->configuration_parse_xml(ctx,service_node,new_service,config);
1338 }
1339 config->services[MAPCACHE_SERVICE_MAPGUIDE] = new_service;
1340 } else if (!strcasecmp(type,"ve")) {
1341 mapcache_service *new_service = mapcache_service_ve_create(ctx);
1342 if(new_service->configuration_parse_xml) {
1343 new_service->configuration_parse_xml(ctx,service_node,new_service,config);
1344 }
1345 config->services[MAPCACHE_SERVICE_VE] = new_service;
1346 } else if (!strcasecmp(type,"demo")) {
1347 mapcache_service *new_service = mapcache_service_demo_create(ctx);
1348 if(new_service->configuration_parse_xml) {
1349 new_service->configuration_parse_xml(ctx,service_node,new_service,config);
1350 }
1351 config->services[MAPCACHE_SERVICE_DEMO] = new_service;
1352 } else {
1353 ctx->set_error(ctx,400,"unknown <service> type %s",type);
1354 }
1355 if(GC_HAS_ERROR(ctx)) goto cleanup;
1356 }
1357 }
1358 } else if ((node = ezxml_child(doc,"services")) != NULL) {
1359 ctx->log(ctx,MAPCACHE_WARN,"<services> tag is deprecated, use <service type=\"wms\" enabled=\"true|false\">");
1360 parseServices(ctx, node, config);
1361 }
1362 if(GC_HAS_ERROR(ctx)) goto cleanup;
1363
1364
1365 node = ezxml_child(doc,"default_format");
1366 if(!node)
1367 node = ezxml_child(doc,"merge_format");
1368 if (node) {
1369 mapcache_image_format *format = mapcache_configuration_get_image_format(config,node->txt);
1370 if(!format) {
1371 ctx->set_error(ctx, 400, "default_format tag references format %s but it is not configured",
1372 node->txt);
1373 goto cleanup;
1374 }
1375 config->default_image_format = format;
1376 }
1377
1378 if ((node = ezxml_child(doc,"errors")) != NULL) {
1379 if(!strcmp(node->txt,"log")) {
1380 config->reporting = MAPCACHE_REPORT_LOG;
1381 } else if(!strcmp(node->txt,"report")) {
1382 config->reporting = MAPCACHE_REPORT_MSG;
1383 } else if(!strcmp(node->txt,"empty_img")) {
1384 config->reporting = MAPCACHE_REPORT_EMPTY_IMG;
1385 mapcache_image_create_empty(ctx, config);
1386 if(GC_HAS_ERROR(ctx)) goto cleanup;
1387 } else if(!strcmp(node->txt, "report_img")) {
1388 config->reporting = MAPCACHE_REPORT_ERROR_IMG;
1389 ctx->set_error(ctx,501,"<errors>: report_img not implemented");
1390 goto cleanup;
1391 } else {
1392 ctx->set_error(ctx,400,"<errors>: unknown value %s (allowed are log, report, empty_img, report_img)",
1393 node->txt);
1394 goto cleanup;
1395 }
1396 }
1397
1398 if((node = ezxml_child(doc,"locker")) != NULL) {
1399 mapcache_config_parse_locker(ctx,node,&config->locker);
1400 GC_CHECK_ERROR(ctx);
1401 } else {
1402 mapcache_config_parse_locker_old(ctx,doc,config);
1403 GC_CHECK_ERROR(ctx);
1404 }
1405
1406 if((node = ezxml_child(doc,"threaded_fetching")) != NULL) {
1407 if(!strcasecmp(node->txt,"true")) {
1408 config->threaded_fetching = 1;
1409 } else if(strcasecmp(node->txt,"false")) {
1410 ctx->set_error(ctx, 400, "failed to parse threaded_fetching \"%s\". Expecting true or false",node->txt);
1411 return;
1412 }
1413 }
1414
1415 if((node = ezxml_child(doc,"log_level")) != NULL) {
1416 if(!strcasecmp(node->txt,"debug")) {
1417 config->loglevel = MAPCACHE_DEBUG;
1418 } else if(!strcasecmp(node->txt,"info")) {
1419 config->loglevel = MAPCACHE_INFO;
1420 } else if(!strcasecmp(node->txt,"notice")) {
1421 config->loglevel = MAPCACHE_NOTICE;
1422 } else if(!strcasecmp(node->txt,"warn")) {
1423 config->loglevel = MAPCACHE_WARN;
1424 } else if(!strcasecmp(node->txt,"error")) {
1425 config->loglevel = MAPCACHE_ERROR;
1426 } else if(!strcasecmp(node->txt,"crit")) {
1427 config->loglevel = MAPCACHE_CRIT;
1428 } else if(!strcasecmp(node->txt,"alert")) {
1429 config->loglevel = MAPCACHE_ALERT;
1430 } else if(!strcasecmp(node->txt,"emerg")) {
1431 config->loglevel = MAPCACHE_EMERG;
1432 } else {
1433 ctx->set_error(ctx,500,"failed to parse <log_level> \"%s\". Expecting debug, info, notice, warn, error, crit, alert or emerg",node->txt);
1434 return;
1435 }
1436 }
1437 if((node = ezxml_child(doc,"auto_reload")) != NULL) {
1438 if(!strcasecmp(node->txt,"true")) {
1439 config->autoreload = 1;
1440 } else if(!strcasecmp(node->txt,"false")) {
1441 config->autoreload = 0;
1442 } else {
1443 ctx->set_error(ctx,500,"failed to parse <auto_reload> \"%s\". Expecting true or false",node->txt);
1444 return;
1445 }
1446 }
1447
1448 config->cp_hmax = 1024;
1449 config->cp_ttl = 60*1000*1000;
1450 if((node = ezxml_child(doc,"connection_pool")) != NULL) {
1451 ezxml_t cp_param_node;
1452 char *endptr;
1453 if ((cp_param_node = ezxml_child(node,"max_connections")) != NULL) {
1454 config->cp_hmax = (int)strtol(cp_param_node->txt,&endptr,10);
1455 if (*endptr != 0 || config->cp_hmax < 0) {
1456 ctx->set_error(ctx, 400, "failed to parse max_connections %s "
1457 "(expecting a positive integer)", cp_param_node->txt);
1458 return;
1459 }
1460 }
1461 if ((cp_param_node = ezxml_child(node,"time_to_live_us")) != NULL) {
1462 config->cp_ttl = (int)strtol(cp_param_node->txt,&endptr,10);
1463 if (*endptr != 0 || config->cp_ttl < 0) {
1464 ctx->set_error(ctx, 400, "failed to parse time_to_live_us %s "
1465 "(expecting a positive integer)", cp_param_node->txt);
1466 return;
1467 }
1468 }
1469 }
1470
1471 cleanup:
1472 ezxml_free(doc);
1473 return;
1474 }
1475 /* vim: ts=2 sts=2 et sw=2
1476 */
1477