1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  MapCache tile caching support file: Mapguide HTTPTile service
6  * Author:   Thomas Bonfort and the MapServer team.
7  *
8  ******************************************************************************
9  * Copyright (c) 1996-2013 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 
35 /** \addtogroup services */
36 
37 /** @{ */
38 
_create_capabilities_mg(mapcache_context * ctx,mapcache_request_get_capabilities * req,char * url,char * path_info,mapcache_cfg * cfg)39 void _create_capabilities_mg(mapcache_context *ctx, mapcache_request_get_capabilities *req, char *url, char *path_info, mapcache_cfg *cfg)
40 {
41   ctx->set_error(ctx, 501, "mapguide service does not support capapbilities");
42 }
43 
44 /**
45  * \brief parse a Mapguide HTTPTile request
46  * \private \memberof mapcache_service_mg
47  * \sa mapcache_service::parse_request()
48  */
_mapcache_service_mg_parse_request(mapcache_context * ctx,mapcache_service * this,mapcache_request ** request,const char * cpathinfo,apr_table_t * params,mapcache_cfg * config)49 void _mapcache_service_mg_parse_request(mapcache_context *ctx, mapcache_service *this, mapcache_request **request,
50     const char *cpathinfo, apr_table_t *params, mapcache_cfg *config)
51 {
52   int index = 0;
53   char *last, *key, *endptr;
54   char *sTileset = NULL;
55   mapcache_tileset *tileset = NULL;
56   mapcache_grid_link *grid_link = NULL;
57   char *pathinfo = NULL;
58   int x=-1,y=-1,z=-1,ygroup=-1,xgroup=1;
59 
60   if(cpathinfo) {
61     pathinfo = apr_pstrdup(ctx->pool,cpathinfo);
62     /* parse a path_info like /1.0.0/global_mosaic/0/0/0.jpg */
63     for (key = apr_strtok(pathinfo, "/", &last); key != NULL;
64          key = apr_strtok(NULL, "/", &last)) {
65       if(!*key) continue; /* skip an empty string, could happen if the url contains // */
66       switch(++index) {
67         case 1: /* S[level] */
68           if(*key!='S') {
69             ctx->set_error(ctx,400, "received mapguide request with invalid level %s", key);
70             return;
71           }
72           z = (int)strtol(key+1,&endptr,10);
73           if(*endptr != 0) {
74             ctx->set_error(ctx,400, "failed to parse S level");
75             return;
76           }
77           break;
78         case 2: /* layer name */
79           sTileset = apr_pstrdup(ctx->pool,key);
80           break;
81         case 3: /*rowgroup*/
82           if(*key!='R') {
83             ctx->set_error(ctx,400, "received mapguide request with invalid rowgroup %s", key);
84             return;
85           }
86           ygroup = (int)strtol(key+1,&endptr,10);
87           if(*endptr != 0) {
88             ctx->set_error(ctx,400, "failed to parse rowgroup");
89             return;
90           }
91           break;
92         case 4: /*colgroup*/
93           if(*key!='C') {
94             ctx->set_error(ctx,400, "received mapguide request with invalid colgroup %s", key);
95             return;
96           }
97           xgroup = (int)strtol(key+1,&endptr,10);
98           if(*endptr != 0) {
99             ctx->set_error(ctx,404, "failed to parse colgroup");
100             return;
101           }
102           break;
103         case 5:
104           y = (int)strtol(key,&endptr,10);
105           if(*endptr != '_') {
106             ctx->set_error(ctx,404, "failed to parse y");
107             return;
108           }
109           key = endptr+1;
110           x = (int)strtol(key,&endptr,10);
111           if(*endptr != '.') {
112             ctx->set_error(ctx,404, "failed to parse x");
113             return;
114           }
115           x += xgroup;
116           y += ygroup;
117           break;
118         default:
119           ctx->set_error(ctx,404, "received mapguide request %s with invalid parameter %s", pathinfo, key);
120           return;
121       }
122     }
123   }
124   if(index == 5) {
125     char *gridname;
126     mapcache_request_get_tile *req = (mapcache_request_get_tile*)apr_pcalloc(ctx->pool,sizeof(mapcache_request_get_tile));
127     ((mapcache_request*)req)->type = MAPCACHE_REQUEST_GET_TILE;
128     gridname = sTileset;  /*hijack the char* pointer while counting the number of commas */
129     while(*gridname) {
130       if(*gridname == ';') req->ntiles++;
131       gridname++;
132     }
133     req->tiles = (mapcache_tile**)apr_pcalloc(ctx->pool,(req->ntiles+1) * sizeof(mapcache_tile*));
134 
135     /* reset the hijacked variables back to default value */
136     gridname = NULL;
137     req->ntiles = 0;
138 
139     for (key = apr_strtok(sTileset, ";", &last); key != NULL;
140          key = apr_strtok(NULL, ";", &last)) {
141       tileset = mapcache_configuration_get_tileset(config,key);
142       if(!tileset) {
143         /*tileset not found directly, test if it was given as "name@grid" notation*/
144         char *tname = apr_pstrdup(ctx->pool,key);
145         char *gname = tname;
146         int i;
147         while(*gname) {
148           if(*gname == '@') {
149             *gname = '\0';
150             gname++;
151             break;
152           }
153           gname++;
154         }
155         if(!gname) {
156           ctx->set_error(ctx,404, "received mapguide request with invalid layer %s", key);
157           return;
158         }
159         tileset = mapcache_configuration_get_tileset(config,tname);
160         if(!tileset) {
161           ctx->set_error(ctx,404, "received mapguide request with invalid layer %s", tname);
162           return;
163         }
164         for(i=0; i<tileset->grid_links->nelts; i++) {
165           mapcache_grid_link *sgrid = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
166           if(!strcmp(sgrid->grid->name,gname)) {
167             grid_link = sgrid;
168             break;
169           }
170         }
171         if(!grid_link) {
172           ctx->set_error(ctx,404, "received mapguide request with invalid grid %s", gname);
173           return;
174         }
175 
176       } else {
177         grid_link = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*);
178       }
179       if(!gridname) {
180         gridname = grid_link->grid->name;
181         z = grid_link->maxz - z - 1;
182         if(z<0 || z>=grid_link->maxz) {
183           ctx->set_error(ctx,404,"invalid z level");
184           return;
185         }
186       } else {
187         if(strcmp(gridname,grid_link->grid->name)) {
188           ctx->set_error(ctx,400,"received mapguide request with conflicting grids %s and %s",
189                          gridname,grid_link->grid->name);
190           return;
191         }
192       }
193       req->tiles[req->ntiles] = mapcache_tileset_tile_create(ctx->pool, tileset, grid_link);
194       switch(grid_link->grid->origin) {
195         case MAPCACHE_GRID_ORIGIN_TOP_LEFT:
196           req->tiles[req->ntiles]->x = x;
197           req->tiles[req->ntiles]->y = y;
198           break;
199         case MAPCACHE_GRID_ORIGIN_BOTTOM_LEFT:
200           req->tiles[req->ntiles]->x = x;
201           req->tiles[req->ntiles]->y = grid_link->grid->levels[z]->maxy - y - 1;
202           break;
203         case MAPCACHE_GRID_ORIGIN_TOP_RIGHT:
204           req->tiles[req->ntiles]->x = grid_link->grid->levels[z]->maxx - x - 1;
205           req->tiles[req->ntiles]->y = y;
206           break;
207         case MAPCACHE_GRID_ORIGIN_BOTTOM_RIGHT:
208           req->tiles[req->ntiles]->x = grid_link->grid->levels[z]->maxx - x - 1;
209           req->tiles[req->ntiles]->y = grid_link->grid->levels[z]->maxy - y - 1;
210           break;
211       }
212       req->tiles[req->ntiles]->z = z;
213       mapcache_tileset_tile_validate(ctx,req->tiles[req->ntiles]);
214       req->ntiles++;
215       GC_CHECK_ERROR(ctx);
216     }
217     *request = (mapcache_request*)req;
218     return;
219   } else {
220     ctx->set_error(ctx,404, "received request with wrong number of arguments", pathinfo);
221     return;
222   }
223 }
224 
_mapcache_service_mg_configuration_xml_parse(mapcache_context * ctx,ezxml_t node,mapcache_service * gservice,mapcache_cfg * cfg)225 void _mapcache_service_mg_configuration_xml_parse(mapcache_context *ctx, ezxml_t node, mapcache_service *gservice, mapcache_cfg *cfg) {
226   const char* attr = ezxml_attr(node,"rows_per_folder");
227   mapcache_service_mapguide *service = (mapcache_service_mapguide*)gservice;
228   char *endptr;
229   if(attr && *attr) {
230     service->rows_per_folder = (int)strtol(attr,&endptr,10);
231     if(*endptr != 0) {
232       ctx->set_error(ctx,400, "failed to parse rows_per_folder attribute");
233       return;
234     }
235   }
236   attr = ezxml_attr(node,"cols_per_folder");
237   if(attr && *attr) {
238     service->cols_per_folder = (int)strtol(attr,&endptr,10);
239     if(*endptr != 0) {
240       ctx->set_error(ctx,400, "failed to parse cols_per_folder attribute");
241       return;
242     }
243   }
244 
245 }
246 
mapcache_service_mapguide_create(mapcache_context * ctx)247 mapcache_service* mapcache_service_mapguide_create(mapcache_context *ctx)
248 {
249   mapcache_service_mapguide* service = (mapcache_service_mapguide*) apr_pcalloc(ctx->pool, sizeof (mapcache_service_mapguide));
250   if (!service) {
251     ctx->set_error(ctx, 500, "failed to allocate mapguide service");
252     return NULL;
253   }
254   service->service.url_prefix = apr_pstrdup(ctx->pool, "mg");
255   service->service.name = apr_pstrdup(ctx->pool, "mapguide");
256   service->service.type = MAPCACHE_SERVICE_MAPGUIDE;
257   service->service.parse_request = _mapcache_service_mg_parse_request;
258   service->service.configuration_parse_xml = _mapcache_service_mg_configuration_xml_parse;
259   service->service.create_capabilities_response = _create_capabilities_mg;
260   service->rows_per_folder = 30;
261   service->cols_per_folder = 30;
262   return (mapcache_service*) service;
263 }
264 
265 /** @} */
266 /* vim: ts=2 sts=2 et sw=2
267 */
268