1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: MapCache tile caching: HTTP Rest cache backend.
6 * Author: Thomas Bonfort and the MapServer team.
7 *
8 ******************************************************************************
9 * Copyright (c) 2014 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 <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <curl/curl.h>
36 #include <apr_base64.h>
37 #include <apr_md5.h>
38 #include <math.h>
39 #include <apr_file_io.h>
40
41 typedef struct mapcache_cache_rest mapcache_cache_rest;
42 typedef struct mapcache_cache_s3 mapcache_cache_s3;
43 typedef struct mapcache_cache_azure mapcache_cache_azure;
44 typedef struct mapcache_cache_google mapcache_cache_google;
45
46 typedef enum {
47 MAPCACHE_REST_METHOD_GET,
48 MAPCACHE_REST_METHOD_HEAD,
49 MAPCACHE_REST_METHOD_PUT,
50 MAPCACHE_REST_METHOD_POST,
51 MAPCACHE_REST_METHOD_DELETE
52 } mapcache_rest_method;
53
54 typedef enum {
55 MAPCACHE_REST_PROVIDER_NONE,
56 MAPCACHE_REST_PROVIDER_S3,
57 MAPCACHE_REST_PROVIDER_AZURE,
58 MAPCACHE_REST_PROVIDER_GOOGLE
59 } mapcache_rest_provider;
60
61 void sha256(const unsigned char *message, unsigned int len, unsigned char *digest);
62 void hmac_sha256(const unsigned char *message, unsigned int message_len,
63 const unsigned char *key, unsigned int key_size,
64 unsigned char *mac, unsigned mac_size);
65 void hmac_sha1(const char *message, unsigned int message_len,
66 const unsigned char *key, unsigned int key_size,
67 void *mac);
68 void sha_hex_encode(unsigned char *sha, unsigned int sha_size);
69 char *base64_encode(apr_pool_t *pool, const unsigned char *data, size_t input_length);
70
71 typedef struct mapcache_rest_operation mapcache_rest_operation;
72 struct mapcache_rest_operation {
73 apr_table_t *headers;
74 mapcache_rest_method method;
75 char *tile_url;
76 char *header_file;
77 void (*add_headers)(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers);
78 };
79
80 typedef struct mapcache_rest_configuration mapcache_rest_configuration;
81 struct mapcache_rest_configuration {
82 apr_table_t *common_headers;
83 char *tile_url;
84 char *header_file;
85 mapcache_rest_operation has_tile;
86 mapcache_rest_operation get_tile;
87 mapcache_rest_operation set_tile;
88 mapcache_rest_operation multi_set_tile;
89 mapcache_rest_operation delete_tile;
90 void (*add_headers)(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers);
91 };
92
93 /**\class mapcache_cache_rest
94 * \brief a mapcache_cache on a 3rd party HTTP Rest API
95 * \implements mapcache_cache
96 */
97 struct mapcache_cache_rest {
98 mapcache_cache cache;
99 mapcache_rest_configuration rest;
100 int use_redirects;
101 int timeout;
102 int connection_timeout;
103 int detect_blank;
104 mapcache_rest_provider provider;
105 };
106
107 struct mapcache_cache_s3 {
108 mapcache_cache_rest cache;
109 char *id;
110 char *secret;
111 char *region;
112 char *credentials_file;
113 };
114
115 struct mapcache_cache_azure {
116 mapcache_cache_rest cache;
117 char *id;
118 char *secret;
119 char *container;
120 };
121
122 struct mapcache_cache_google {
123 mapcache_cache_rest cache;
124 char *access;
125 char *secret;
126 };
127
128 typedef struct {
129 mapcache_buffer *buffer;
130 size_t offset;
131 } buffer_struct;
132
133 struct rest_conn_params {
134 mapcache_cache_rest *cache;
135 };
136
mapcache_rest_connection_constructor(mapcache_context * ctx,void ** conn_,void * params)137 void mapcache_rest_connection_constructor(mapcache_context *ctx, void **conn_, void *params) {
138 CURL *curl_handle = curl_easy_init();
139 if(!curl_handle) {
140 ctx->set_error(ctx,500,"failed to create curl handle");
141 *conn_ = NULL;
142 return;
143 }
144 *conn_ = curl_handle;
145 }
146
mapcache_rest_connection_destructor(void * conn_)147 void mapcache_rest_connection_destructor(void *conn_) {
148 CURL *curl_handle = (CURL*) conn_;
149 curl_easy_cleanup(curl_handle);
150 }
151
_rest_get_connection(mapcache_context * ctx,mapcache_cache_rest * cache,mapcache_tile * tile)152 static mapcache_pooled_connection* _rest_get_connection(mapcache_context *ctx, mapcache_cache_rest *cache, mapcache_tile *tile)
153 {
154 mapcache_pooled_connection *pc;
155 struct rest_conn_params params;
156
157 params.cache = cache;
158
159 pc = mapcache_connection_pool_get_connection(ctx,cache->cache.name,mapcache_rest_connection_constructor,
160 mapcache_rest_connection_destructor, ¶ms);
161 if(!GC_HAS_ERROR(ctx) && pc && pc->connection) {
162 CURL *curl_handle = (CURL*)pc->connection;
163 curl_easy_reset(curl_handle);
164 curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, cache->connection_timeout);
165 curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, cache->timeout);
166 }
167
168 return pc;
169 }
170
buffer_read_callback(void * ptr,size_t size,size_t nmemb,void * stream)171 static size_t buffer_read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
172 {
173 buffer_struct *buffer = (buffer_struct*)stream;
174 void *start = ((char*)(buffer->buffer->buf)) + buffer->offset;
175 size_t bytes = MAPCACHE_MIN((buffer->buffer->size-buffer->offset),(size * nmemb));
176 if(bytes) {
177 memcpy(ptr,start,bytes);
178 buffer->offset += bytes;
179 }
180 return bytes;
181 }
182
buffer_write_callback(void * ptr,size_t size,size_t nmemb,void * data)183 size_t buffer_write_callback(void *ptr, size_t size, size_t nmemb, void *data)
184 {
185 mapcache_buffer *buffer = (mapcache_buffer*)data;
186 size_t realsize = size * nmemb;
187 return mapcache_buffer_append(buffer, realsize, ptr);
188 }
189
_set_headers(mapcache_context * ctx,CURL * curl,apr_table_t * headers)190 static struct curl_slist* _set_headers(mapcache_context *ctx, CURL *curl, apr_table_t *headers) {
191 if(!headers) {
192 return NULL;
193 } else {
194 struct curl_slist *curl_headers=NULL;
195 const apr_array_header_t *array = apr_table_elts(headers);
196 apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
197 int i;
198 for (i = 0; i < array->nelts; i++) {
199 if(strlen(elts[i].val) > 0) {
200 curl_headers = curl_slist_append(curl_headers, apr_pstrcat(ctx->pool,elts[i].key,": ",elts[i].val,NULL));
201 } else {
202 curl_headers = curl_slist_append(curl_headers, apr_pstrcat(ctx->pool,elts[i].key,":",NULL));
203 }
204 }
205 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers);
206 return curl_headers;
207 }
208 }
209
_put_request(mapcache_context * ctx,CURL * curl,mapcache_buffer * buffer,char * url,apr_table_t * headers)210 static void _put_request(mapcache_context *ctx, CURL *curl, mapcache_buffer *buffer, char *url, apr_table_t *headers) {
211 CURLcode res;
212 buffer_struct data;
213 mapcache_buffer *response;
214 struct curl_slist *curl_header_data;
215
216 data.buffer = buffer;
217 data.offset = 0;
218
219 response = mapcache_buffer_create(10,ctx->pool);
220
221 #if LIBCURL_VERSION_NUM < 0x071700
222 /*
223 * hack around a bug in curl <= 7.22 where the content-length is added
224 * a second time even if ti was present in the manually set headers
225 */
226 apr_table_unset(headers, "Content-Length");
227 #endif
228
229
230 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
231
232 /* we want to use our own read function */
233 curl_easy_setopt(curl, CURLOPT_READFUNCTION, buffer_read_callback);
234
235 /* enable uploading */
236 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
237
238 /* HTTP PUT please */
239 curl_easy_setopt(curl, CURLOPT_PUT, 1L);
240
241 /* don't use an Expect: 100 Continue header */
242 apr_table_set(headers, "Expect", "");
243 curl_header_data = _set_headers(ctx, curl, headers);
244
245 /* specify target URL, and note that this URL should include a file
246 * name, not only a directory */
247 curl_easy_setopt(curl, CURLOPT_URL, url);
248
249 /* now specify which file to upload */
250 curl_easy_setopt(curl, CURLOPT_READDATA, &data);
251
252 /* provide the size of the upload, we specicially typecast the value
253 * to curl_off_t since we must be sure to use the correct data size */
254 curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer->size);
255
256 /* send all data to this function */
257 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, buffer_write_callback);
258
259 /* we pass our mapcache_buffer struct to the callback function */
260 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response);
261
262 /* Now run off and do what you've been told! */
263 res = curl_easy_perform(curl);
264 /* Check for errors */
265 if(res != CURLE_OK) {
266 ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest put: %s",curl_easy_strerror(res));
267 } else {
268 long http_code;
269 curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
270 if(http_code != 200 && http_code != 201 && http_code != 204) {
271 char *msg = response->buf;
272 msg[response->size]=0;
273 ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest put with code %ld: %s", http_code, msg);
274 }
275 }
276
277 curl_slist_free_all(curl_header_data);
278 }
279
_head_request(mapcache_context * ctx,CURL * curl,char * url,apr_table_t * headers)280 static int _head_request(mapcache_context *ctx, CURL *curl, char *url, apr_table_t *headers) {
281
282 CURLcode res;
283 long http_code;
284 struct curl_slist *curl_header_data;
285
286 curl_header_data = _set_headers(ctx, curl, headers);
287
288 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
289
290 /* specify target URL, and note that this URL should include a file
291 * name, not only a directory */
292 curl_easy_setopt(curl, CURLOPT_URL, url);
293
294 curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
295
296 /* Now run off and do what you've been told! */
297 res = curl_easy_perform(curl);
298 /* Check for errors */
299 if(res != CURLE_OK) {
300 ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest head %s",curl_easy_strerror(res));
301 http_code = 500;
302 } else {
303 curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
304 }
305
306 curl_slist_free_all(curl_header_data);
307
308 return (int)http_code;
309 }
310
_delete_request(mapcache_context * ctx,CURL * curl,char * url,apr_table_t * headers)311 static int _delete_request(mapcache_context *ctx, CURL *curl, char *url, apr_table_t *headers) {
312
313 CURLcode res;
314 long http_code;
315 struct curl_slist *curl_header_data;
316
317 curl_header_data = _set_headers(ctx, curl, headers);
318
319 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
320
321 /* specify target URL, and note that this URL should include a file
322 * name, not only a directory */
323 curl_easy_setopt(curl, CURLOPT_URL, url);
324
325 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
326 curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
327
328 /* Now run off and do what you've been told! */
329 res = curl_easy_perform(curl);
330 /* Check for errors */
331 if(res != CURLE_OK) {
332 ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest delete %s",curl_easy_strerror(res));
333 http_code = 500;
334 } else {
335 curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
336 }
337
338 curl_slist_free_all(curl_header_data);
339
340 return (int)http_code;
341 }
342
_get_request(mapcache_context * ctx,CURL * curl,char * url,apr_table_t * headers)343 static mapcache_buffer* _get_request(mapcache_context *ctx, CURL *curl, char *url, apr_table_t *headers) {
344
345 CURLcode res;
346 mapcache_buffer *data = NULL;
347 long http_code;
348 struct curl_slist *curl_header_data;
349
350 curl_header_data = _set_headers(ctx, curl, headers);
351
352 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
353
354 data = mapcache_buffer_create(4000, ctx->pool);
355
356 /* send all data to this function */
357 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, buffer_write_callback);
358
359 /* we pass our mapcache_buffer struct to the callback function */
360 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)data);
361
362 /* specify target URL, and note that this URL should include a file
363 * name, not only a directory */
364 curl_easy_setopt(curl, CURLOPT_URL, url);
365
366 /* Now run off and do what you've been told! */
367 res = curl_easy_perform(curl);
368 /* Check for errors */
369 if(res != CURLE_OK) {
370 ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest get: %s",curl_easy_strerror(res));
371 data = NULL;
372 } else {
373 curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
374 /* handle special behavior of s3 */
375 if(http_code == 403) {
376 char *msg = data->buf;
377 while(msg && *msg) {
378 if(!strncmp(msg,"NoSuchKey",strlen("NoSuchKey"))) {
379 ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest get with code %ld: %s", http_code, msg);
380 http_code = 404;
381 data = NULL;
382 break;
383 }
384 msg++;
385 }
386 }
387 if(http_code != 200 && http_code != 404) {
388 char *msg = data->buf;
389 msg[data->size]=0;
390 ctx->set_error(ctx, 500, "curl_easy_perform() failed in rest get with code %ld: %s", http_code, msg);
391 }
392 if(http_code == 404) {
393 data = NULL; /* not an error */
394 }
395 }
396
397 curl_slist_free_all(curl_header_data);
398
399 return data;
400 }
401
402 /**
403 * @brief _mapcache_cache_rest_add_headers_from_file populate header table from entries found in file
404 * @param ctx
405 * @param file the file from which headers should be read
406 * @param headers the output table which will be populated
407 */
_mapcache_cache_rest_add_headers_from_file(mapcache_context * ctx,char * file,apr_table_t * headers)408 void _mapcache_cache_rest_add_headers_from_file(mapcache_context *ctx, char *file, apr_table_t *headers) {
409 apr_status_t rv;
410 apr_file_t *f;
411 if((rv=apr_file_open(&f, file, APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT,
412 ctx->pool)) == APR_SUCCESS) {
413 char line[8096];
414 while( (rv = apr_file_gets(line,8096,f))== APR_SUCCESS) {
415 char *header_name=line, *header_val=line, *header_endval;
416 int found_token = MAPCACHE_FALSE;
417 /*search for header delimiter (:)*/
418 while(header_val && *header_val) {
419 if(*header_val == ':') {
420 *header_val = '\0';
421 found_token = MAPCACHE_TRUE;
422 break;
423 }
424 header_val++;
425 }
426 if(!found_token) {
427 /* malformed line, silently skip it */
428 continue;
429 }
430
431 header_val++;
432
433 if(!*header_val) {
434 /* malformed/empty line, skip it */
435 continue;
436 }
437
438 header_endval = header_val;
439 while(*header_endval) {
440 if(*header_endval == '\r' || *header_endval == '\n') {
441 *header_endval = '\0';
442 break;
443 }
444 header_endval++;
445 }
446
447 if(!*header_val) {
448 /* empty header value, skip it */
449 continue;
450 }
451
452 apr_table_set(headers, header_name, header_val);
453 }
454 apr_file_close(f);
455 } else {
456 ctx->set_error(ctx,500,"rest cache: failed to access header file");
457 }
458 }
459
_mapcache_cache_rest_headers(mapcache_context * ctx,mapcache_tile * tile,mapcache_rest_configuration * config,mapcache_rest_operation * operation)460 apr_table_t* _mapcache_cache_rest_headers(mapcache_context *ctx, mapcache_tile *tile, mapcache_rest_configuration *config,
461 mapcache_rest_operation *operation) {
462 apr_table_t *ret = apr_table_make(ctx->pool,3);
463 const apr_array_header_t *array;
464
465 if(config->common_headers) {
466 apr_table_entry_t *elts;
467 int i;
468 array = apr_table_elts(config->common_headers);
469 elts = (apr_table_entry_t *) array->elts;
470 for (i = 0; i < array->nelts; i++) {
471 apr_table_set(ret, elts[i].key,elts[i].val);
472 }
473 }
474 if(config->header_file) {
475 _mapcache_cache_rest_add_headers_from_file(ctx,config->header_file,ret);
476 if(GC_HAS_ERROR(ctx)) {
477 return NULL;
478 }
479 }
480 if(operation->headers) {
481 apr_table_entry_t *elts;
482 int i;
483 array = apr_table_elts(operation->headers);
484 elts = (apr_table_entry_t *) array->elts;
485 for (i = 0; i < array->nelts; i++) {
486 apr_table_set(ret, elts[i].key,elts[i].val);
487 }
488 }
489 if(operation->header_file) {
490 _mapcache_cache_rest_add_headers_from_file(ctx,operation->header_file,ret);
491 if(GC_HAS_ERROR(ctx)) {
492 return NULL;
493 }
494 }
495 return ret;
496 }
497
498 /* Converts an integer value to its hex character*/
to_hex(char code)499 static char to_hex(char code) {
500 static char hex[] = "0123456789ABCDEF";
501 return hex[code & 15];
502 }
503
504 /* Returns a url-encoded version of str */
url_encode(apr_pool_t * pool,char * str)505 static char *url_encode(apr_pool_t *pool, char *str) {
506 char *pstr = str, *buf = apr_pcalloc(pool, strlen(str) * 3 + 1), *pbuf = buf;
507 while (*pstr) {
508 if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~' || *pstr=='/')
509 *pbuf++ = *pstr;
510 else if (*pstr == ' ')
511 *pbuf++ = '+';
512 else
513 *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
514 pstr++;
515 }
516 *pbuf = '\0';
517 return buf;
518 }
519 /**
520 * \brief return url for given tile given a template
521 *
522 * \param tile the tile to get the key from
523 * \param template the template to build the url from
524 * \param path pointer to a char* that will contain the url
525 * \param r
526 * \private \memberof mapcache_cache_rest
527 */
_mapcache_cache_rest_tile_url(mapcache_context * ctx,mapcache_tile * tile,mapcache_rest_configuration * config,mapcache_rest_operation * operation,char ** url)528 static void _mapcache_cache_rest_tile_url(mapcache_context *ctx, mapcache_tile *tile, mapcache_rest_configuration *config,
529 mapcache_rest_operation *operation, char **url)
530 {
531 char *slashptr,*path;
532 int cnt=0;
533 if(operation && operation->tile_url) {
534 *url = apr_pstrdup(ctx->pool, operation->tile_url);
535 } else {
536 *url = apr_pstrdup(ctx->pool, config->tile_url);
537 }
538
539 *url = mapcache_util_str_replace(ctx->pool, *url, "{tileset}", tile->tileset->name);
540 *url = mapcache_util_str_replace(ctx->pool, *url, "{grid}", tile->grid_link->grid->name);
541 *url = mapcache_util_str_replace(ctx->pool, *url, "{ext}",
542 tile->tileset->format?tile->tileset->format->extension:"png");
543 if(strstr(*url,"{x}"))
544 *url = mapcache_util_str_replace(ctx->pool,*url, "{x}",
545 apr_psprintf(ctx->pool,"%d",tile->x));
546 else
547 *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_x}",
548 apr_psprintf(ctx->pool,"%d",
549 tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1));
550 if(strstr(*url,"{y}"))
551 *url = mapcache_util_str_replace(ctx->pool,*url, "{y}",
552 apr_psprintf(ctx->pool,"%d",tile->y));
553 else
554 *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_y}",
555 apr_psprintf(ctx->pool,"%d",
556 tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1));
557 if(strstr(*url,"{z}"))
558 *url = mapcache_util_str_replace(ctx->pool,*url, "{z}",
559 apr_psprintf(ctx->pool,"%d",tile->z));
560 else
561 *url = mapcache_util_str_replace(ctx->pool,*url, "{inv_z}",
562 apr_psprintf(ctx->pool,"%d",
563 tile->grid_link->grid->nlevels - tile->z - 1));
564 if(tile->dimensions) {
565 int i;
566 if(strstr(*url,"{dim")) {
567 char *dimstring="";
568 i = tile->dimensions->nelts;
569 while(i--) {
570 char *single_dim;
571 mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
572 if(!entry->cached_value) {
573 ctx->set_error(ctx,500,"BUG: dimension (%s) not defined",entry->dimension->name);
574 return;
575 }
576 dimstring = apr_pstrcat(ctx->pool,dimstring,"#",entry->dimension->name,"#",entry->cached_value,NULL);
577 single_dim = apr_pstrcat(ctx->pool,"{dim:",entry->dimension->name,"}",NULL);
578 if(strstr(*url,single_dim)) {
579 *url = mapcache_util_str_replace(ctx->pool,*url, single_dim, entry->cached_value);
580 }
581 }
582 *url = mapcache_util_str_replace(ctx->pool,*url, "{dim}", dimstring);
583 }
584 }
585 /* url-encode everything after the host name */
586
587 /* find occurence of third "/" in url */
588 slashptr = *url;
589 while(*slashptr) {
590 if(*slashptr == '/') cnt++;
591 if(cnt == 3) break;
592 slashptr++;
593 }
594 if(!*slashptr) {
595 ctx->set_error(ctx,500,"invalid rest url provided, expecting http(s)://server/path format");
596 return;
597 }
598 path=slashptr;
599 path = url_encode(ctx->pool,path);
600 *slashptr=0;
601
602
603 *url = apr_pstrcat(ctx->pool,*url,path,NULL);
604 /*ctx->log(ctx,MAPCACHE_DEBUG,"rest url: %s",*url);*/
605 }
606
607
608 // Simple comparison function for comparing two HTTP header names that are
609 // embedded within an HTTP header line, returning true if header1 comes
610 // before header2 alphabetically, false if not
headerle(const char * header1,const char * header2)611 static int headerle(const char *header1, const char *header2)
612 {
613 while (1) {
614 if (*header1 == ':') {
615 return (*header2 == ':');
616 }
617 else if (*header2 == ':') {
618 return 0;
619 }
620 else if (*header2 < *header1) {
621 return 0;
622 }
623 else if (*header2 > *header1) {
624 return 1;
625 }
626 header1++, header2++;
627 }
628 }
629
630
631 // Replace this with merge sort eventually, it's the best stable sort. But
632 // since typically the number of elements being sorted is small, it doesn't
633 // matter that much which sort is used, and gnome sort is the world's simplest
634 // stable sort. Added a slight twist to the standard gnome_sort - don't go
635 // forward +1, go forward to the last highest index considered. This saves
636 // all the string comparisons that would be done "going forward", and thus
637 // only does the necessary string comparisons to move values back into their
638 // sorted position.
header_gnome_sort(char ** headers,int size)639 static void header_gnome_sort(char **headers, int size)
640 {
641 int i = 0, last_highest = 0;
642
643 while (i < size) {
644 if ((i == 0) || headerle(headers[i - 1], headers[i])) {
645 i = ++last_highest;
646 }
647 else {
648 char *tmp = headers[i];
649 headers[i] = headers[i - 1];
650 headers[--i] = tmp;
651 }
652 }
653 }
654
my_apr_table_get(apr_table_t * t,char * key)655 static const char* my_apr_table_get(apr_table_t *t, char *key) {
656 const char *val = apr_table_get(t,key);
657 if(!val) val = "";
658 return val;
659 }
660
661
_mapcache_cache_google_headers_add(mapcache_context * ctx,const char * method,mapcache_cache_rest * rcache,mapcache_tile * tile,char * url,apr_table_t * headers)662 static void _mapcache_cache_google_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
663 {
664 char *stringToSign, **aheaders, *resource = url, x_amz_date[64];
665 const char *head;
666 const apr_array_header_t *ahead;
667 apr_table_entry_t *elts;
668 mapcache_cache_google *google;
669 time_t now;
670 struct tm *tnow;
671 unsigned char sha[65];
672 char b64[150];
673 int i,nCanonicalHeaders=0,cnt=0;
674 assert(rcache->provider == MAPCACHE_REST_PROVIDER_GOOGLE);
675 google = (mapcache_cache_google*)rcache;
676 now = time(NULL);
677 tnow = gmtime(&now);
678 sha[64]=0;
679
680 strftime(x_amz_date, 64 , "%a, %d %b %Y %H:%M:%S GMT", tnow);
681 apr_table_set(headers,"x-amz-date",x_amz_date);
682
683 if(!strcmp(method,"PUT")) {
684 assert(tile->encoded_data);
685 apr_md5(sha,tile->encoded_data->buf,tile->encoded_data->size);
686 apr_base64_encode(b64, (char*)sha, 16);
687 apr_table_set(headers, "Content-MD5", b64);
688 }
689
690 head = my_apr_table_get(headers, "Content-MD5");
691 stringToSign=apr_pstrcat(ctx->pool, method, "\n", head, "\n", NULL);
692 head = my_apr_table_get(headers, "Content-Type");
693 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
694
695 /* Date: header, left empty as we are using x-amz-date */
696 stringToSign=apr_pstrcat(ctx->pool, stringToSign, "\n", NULL);
697
698 ahead = apr_table_elts(headers);
699 aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*));
700 elts = (apr_table_entry_t *) ahead->elts;
701
702 for (i = 0; i < ahead->nelts; i++) {
703 if(!strncmp(elts[i].key,"x-amz-",6)) {
704 char *k = aheaders[nCanonicalHeaders] = apr_pstrdup(ctx->pool, elts[i].key);
705 while(*k) {
706 *k = tolower(*k);
707 k++;
708 }
709 nCanonicalHeaders++;
710 }
711 }
712 header_gnome_sort(aheaders, nCanonicalHeaders);
713
714 for(i=0; i<nCanonicalHeaders; i++) {
715 stringToSign = apr_pstrcat(ctx->pool, stringToSign, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL);
716 }
717
718 /* find occurence of third "/" in url */
719 while(*resource) {
720 if(*resource == '/') cnt++;
721 if(cnt == 3) break;
722 resource++;
723 }
724 if(!*resource) {
725 ctx->set_error(ctx,500,"invalid google url provided");
726 return;
727 }
728
729 stringToSign = apr_pstrcat(ctx->pool, stringToSign, resource, NULL);
730
731 hmac_sha1(stringToSign, strlen(stringToSign), (unsigned char*)google->secret, strlen(google->secret), sha);
732
733 apr_base64_encode(b64, (char*)sha, 20);
734
735
736 apr_table_set( headers, "Authorization", apr_pstrcat(ctx->pool,"AWS ", google->access, ":", b64, NULL));
737 }
738
_mapcache_cache_azure_headers_add(mapcache_context * ctx,const char * method,mapcache_cache_rest * rcache,mapcache_tile * tile,char * url,apr_table_t * headers)739 static void _mapcache_cache_azure_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
740 {
741 char *stringToSign, **aheaders, *canonical_headers="", *canonical_resource=NULL, *resource = url, x_ms_date[64];
742 const char *head;
743 const apr_array_header_t *ahead;
744 apr_table_entry_t *elts;
745 mapcache_cache_azure *azure;
746 time_t now;
747 struct tm *tnow;
748 unsigned char sha[65];
749 char *b64sign,*keyub64;
750 int i,nCanonicalHeaders=0,cnt=0;
751 assert(rcache->provider == MAPCACHE_REST_PROVIDER_AZURE);
752 azure = (mapcache_cache_azure*)rcache;
753 now = time(NULL);
754 tnow = gmtime(&now);
755 sha[64]=0;
756
757 strftime(x_ms_date, sizeof(x_ms_date), "%a, %d %b %Y %H:%M:%S GMT", tnow);
758 apr_table_set(headers,"x-ms-date",x_ms_date);
759 apr_table_set(headers,"x-ms-version","2009-09-19");
760 apr_table_set(headers,"x-ms-blob-type","BlockBlob");
761
762 stringToSign = apr_pstrcat(ctx->pool, method, "\n", NULL);
763 head = my_apr_table_get(headers, "Content-Encoding");
764 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
765 head = my_apr_table_get(headers, "Content-Language");
766 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
767 head = my_apr_table_get(headers, "Content-Length");
768 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
769 head = my_apr_table_get(headers, "Content-MD5");
770 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
771 head = my_apr_table_get(headers, "Content-Type");
772 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
773 head = my_apr_table_get(headers, "Date");
774 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
775 head = my_apr_table_get(headers, "If-Modified-Since");
776 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
777 head = my_apr_table_get(headers, "If-Match");
778 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
779 head = my_apr_table_get(headers, "If-None-Match");
780 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
781 head = my_apr_table_get(headers, "If-Unmodified-Since");
782 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
783 head = my_apr_table_get(headers, "Range");
784 stringToSign=apr_pstrcat(ctx->pool, stringToSign, head, "\n", NULL);
785
786 ahead = apr_table_elts(headers);
787 aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*));
788 elts = (apr_table_entry_t *) ahead->elts;
789
790 for (i = 0; i < ahead->nelts; i++) {
791 char *k;
792 if(strncmp(elts[i].key,"x-ms-",5) || elts[i].key[5]==0) continue;
793 k = aheaders[nCanonicalHeaders] = apr_pstrdup(ctx->pool, elts[i].key);
794 while(*k) {
795 *k = tolower(*k);
796 k++;
797 }
798 nCanonicalHeaders++;
799 }
800 header_gnome_sort(aheaders, nCanonicalHeaders);
801
802 for(i=0; i<nCanonicalHeaders; i++) {
803 canonical_headers = apr_pstrcat(ctx->pool, canonical_headers, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL);
804 }
805
806 /* find occurence of third "/" in url */
807 while(*resource) {
808 if(*resource == '/') cnt++;
809 if(cnt == 3) break;
810 resource++;
811 }
812 if(!*resource) {
813 ctx->set_error(ctx,500,"invalid azure url provided");
814 return;
815 }
816
817 canonical_resource = apr_pstrcat(ctx->pool, "/", azure->id, resource, NULL);
818
819 stringToSign = apr_pstrcat(ctx->pool, stringToSign, canonical_headers, canonical_resource, NULL);
820
821 keyub64 = (char*)apr_pcalloc(ctx->pool, apr_base64_decode_len(azure->secret));
822 apr_base64_decode(keyub64, azure->secret);
823
824 hmac_sha256((unsigned char*)stringToSign, strlen(stringToSign), (unsigned char*)keyub64, strlen(keyub64), sha, 32);
825
826 b64sign = (char*)apr_pcalloc(ctx->pool, apr_base64_encode_len(32));
827 apr_base64_encode(b64sign, (char*)sha, 32);
828
829
830 apr_table_set( headers, "Authorization", apr_pstrcat(ctx->pool,"SharedKey ", azure->id, ":", b64sign, NULL));
831
832
833 }
834
_remove_lineends(char * str)835 static void _remove_lineends(char *str) {
836 if(str) {
837 size_t len = strlen(str);
838 while(len>0) {
839 if(str[len-1] == '\n' || str[len-1] == '\r') {
840 str[len-1] = '\0';
841 len--;
842 } else {
843 break;
844 }
845 }
846 }
847 }
848
_mapcache_cache_s3_headers_add(mapcache_context * ctx,const char * method,mapcache_cache_rest * rcache,mapcache_tile * tile,char * url,apr_table_t * headers)849 static void _mapcache_cache_s3_headers_add(mapcache_context *ctx, const char* method, mapcache_cache_rest *rcache, mapcache_tile *tile, char *url, apr_table_t *headers)
850 {
851 unsigned char sha1[65],sha2[65];
852 int cnt=0,i;
853 time_t now = time(NULL);
854 struct tm *tnow = gmtime(&now);
855 const apr_array_header_t *ahead;
856 char *tosign, *key, *canonical_request, x_amz_date[64], *resource = url, **aheaders, *auth;
857 apr_table_entry_t *elts;
858 mapcache_cache_s3 *s3;
859 char *aws_access_key_id = NULL, *aws_secret_access_key = NULL, *aws_security_token = NULL;
860
861 sha1[64]=sha2[64]=0;
862 assert(rcache->provider == MAPCACHE_REST_PROVIDER_S3);
863 s3 = (mapcache_cache_s3*)rcache;
864
865 if(s3->credentials_file) {
866 apr_status_t rv;
867 apr_file_t *f;
868 if((rv=apr_file_open(&f, s3->credentials_file,
869 APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT,
870 ctx->pool)) == APR_SUCCESS) {
871 char line[1024];
872 if( (rv = apr_file_gets(line,1024,f))== APR_SUCCESS) {
873 _remove_lineends(line);
874 aws_access_key_id = apr_pstrdup(ctx->pool,line);
875 }
876 if( (rv = apr_file_gets(line,1024,f))== APR_SUCCESS) {
877 _remove_lineends(line);
878 aws_secret_access_key = apr_pstrdup(ctx->pool,line);
879 }
880 if( (rv = apr_file_gets(line,1024,f))== APR_SUCCESS) {
881 _remove_lineends(line);
882 aws_security_token = apr_pstrdup(ctx->pool,line);
883 }
884 apr_file_close(f);
885 if(!aws_access_key_id || !*aws_access_key_id|| !aws_secret_access_key ||!*aws_secret_access_key) {
886 ctx->set_error(ctx,500,"failed to read access or secret key from credentials file");
887 }
888 if(aws_security_token && !*aws_security_token) {
889 aws_security_token = NULL;
890 }
891 } else {
892 ctx->set_error(ctx,500,"failed to access S3 credential config");
893 }
894 } else {
895 aws_access_key_id = s3->id;
896 aws_secret_access_key = s3->secret;
897 aws_security_token = NULL;
898 }
899
900 if(!strcmp(method,"PUT")) {
901 assert(tile->encoded_data);
902 sha256((unsigned char*)tile->encoded_data->buf, tile->encoded_data->size, sha1);
903 sha_hex_encode(sha1,32);
904 } else {
905 /* sha256 hash of empty string */
906 memcpy(sha1,"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",64);
907 }
908 apr_table_set(headers,"x-amz-content-sha256", (char*)sha1);
909 /* sha1 contains the hash of the payload */
910
911 /* find occurence of third "/" in url */
912 while(*resource) {
913 if(*resource == '/') cnt++;
914 if(cnt == 3) break;
915 resource++;
916 }
917 if(!*resource) {
918 ctx->set_error(ctx,500,"invalid s3 url provided");
919 return;
920 }
921
922 strftime(x_amz_date, sizeof(x_amz_date), "%Y%m%dT%H%M%SZ", tnow);
923 apr_table_set(headers, "x-amz-date", x_amz_date);
924
925 if(aws_security_token) {
926 apr_table_set(headers, "x-amz-security-token", aws_security_token);
927 }
928
929 canonical_request = apr_pstrcat(ctx->pool, method, "\n" ,resource, "\n\n",NULL);
930
931 ahead = apr_table_elts(headers);
932 aheaders = apr_pcalloc(ctx->pool, ahead->nelts * sizeof(char*));
933 elts = (apr_table_entry_t *) ahead->elts;
934
935 for (i = 0; i < ahead->nelts; i++) {
936 char *k = aheaders[i] = apr_pstrdup(ctx->pool, elts[i].key);
937 while(*k) {
938 *k = tolower(*k);
939 k++;
940 }
941 }
942
943 header_gnome_sort(aheaders, ahead->nelts);
944 for(i=0; i<ahead->nelts; i++) {
945 canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],":",apr_table_get(headers,aheaders[i]),"\n",NULL);
946 }
947 canonical_request = apr_pstrcat(ctx->pool, canonical_request, "\n", NULL);
948 for(i=0; i<ahead->nelts; i++) {
949 if(i==ahead->nelts-1) {
950 canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],NULL);
951 } else {
952 canonical_request = apr_pstrcat(ctx->pool, canonical_request, aheaders[i],";",NULL);
953 }
954 }
955 canonical_request = apr_pstrcat(ctx->pool, canonical_request, "\n", sha1, NULL);
956 //printf("canonical request: %s\n",canonical_request);
957
958 tosign = apr_pstrcat(ctx->pool, "AWS4-HMAC-SHA256\n",x_amz_date,"\n",NULL);
959 x_amz_date[8]=0;
960 sha256((unsigned char*)canonical_request, strlen(canonical_request), sha1);
961 sha_hex_encode(sha1,32);
962 tosign = apr_pstrcat(ctx->pool, tosign, x_amz_date, "/", s3->region, "/s3/aws4_request\n", sha1,NULL);
963 //printf("key to sign: %s\n",tosign);
964
965 key = apr_pstrcat(ctx->pool, "AWS4", aws_secret_access_key, NULL);
966 hmac_sha256((unsigned char*)x_amz_date, 8, (unsigned char*)key, strlen(key), sha1, 32);
967 hmac_sha256((unsigned char*)s3->region, strlen(s3->region), sha1, 32, sha2, 32);
968 hmac_sha256((unsigned char*)"s3", 2, sha2, 32, sha1, 32);
969 hmac_sha256((unsigned char*)"aws4_request", 12, sha1, 32, sha2, 32);
970 hmac_sha256((unsigned char*)tosign, strlen(tosign), sha2, 32, sha1, 32);
971 sha_hex_encode(sha1,32);
972
973
974 auth = apr_pstrcat(ctx->pool, "AWS4-HMAC-SHA256 Credential=",aws_access_key_id,"/",x_amz_date,"/",s3->region,"/s3/aws4_request,SignedHeaders=",NULL);
975
976 for(i=0; i<ahead->nelts; i++) {
977 if(i==ahead->nelts-1) {
978 auth = apr_pstrcat(ctx->pool, auth, aheaders[i],NULL);
979 } else {
980 auth = apr_pstrcat(ctx->pool, auth, aheaders[i],";",NULL);
981 }
982 }
983 auth = apr_pstrcat(ctx->pool, auth, ",Signature=", sha1, NULL);
984
985 apr_table_set(headers, "Authorization", auth);
986 }
987
_mapcache_cache_s3_put_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)988 static void _mapcache_cache_s3_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
989 _mapcache_cache_s3_headers_add(ctx, "PUT", pcache, tile, url, headers);
990 }
_mapcache_cache_s3_get_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)991 static void _mapcache_cache_s3_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
992 _mapcache_cache_s3_headers_add(ctx, "GET", pcache, tile, url, headers);
993 }
_mapcache_cache_s3_head_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)994 static void _mapcache_cache_s3_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
995 _mapcache_cache_s3_headers_add(ctx, "HEAD", pcache, tile, url, headers);
996 }
_mapcache_cache_s3_delete_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)997 static void _mapcache_cache_s3_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
998 _mapcache_cache_s3_headers_add(ctx, "DELETE", pcache, tile, url, headers);
999 }
_mapcache_cache_azure_put_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)1000 static void _mapcache_cache_azure_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
1001 _mapcache_cache_azure_headers_add(ctx, "PUT", pcache, tile, url, headers);
1002 }
_mapcache_cache_azure_get_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)1003 static void _mapcache_cache_azure_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
1004 _mapcache_cache_azure_headers_add(ctx, "GET", pcache, tile, url, headers);
1005 }
_mapcache_cache_azure_head_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)1006 static void _mapcache_cache_azure_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
1007 _mapcache_cache_azure_headers_add(ctx, "HEAD", pcache, tile, url, headers);
1008 }
_mapcache_cache_azure_delete_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)1009 static void _mapcache_cache_azure_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
1010 _mapcache_cache_azure_headers_add(ctx, "DELETE", pcache, tile, url, headers);
1011 }
_mapcache_cache_google_put_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)1012 static void _mapcache_cache_google_put_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
1013 _mapcache_cache_google_headers_add(ctx, "PUT", pcache, tile, url, headers);
1014 }
_mapcache_cache_google_get_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)1015 static void _mapcache_cache_google_get_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
1016 _mapcache_cache_google_headers_add(ctx, "GET", pcache, tile, url, headers);
1017 }
_mapcache_cache_google_head_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)1018 static void _mapcache_cache_google_head_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
1019 _mapcache_cache_google_headers_add(ctx, "HEAD", pcache, tile, url, headers);
1020 }
_mapcache_cache_google_delete_headers_add(mapcache_context * ctx,mapcache_cache_rest * pcache,mapcache_tile * tile,char * url,apr_table_t * headers)1021 static void _mapcache_cache_google_delete_headers_add(mapcache_context *ctx, mapcache_cache_rest *pcache, mapcache_tile *tile, char *url, apr_table_t *headers) {
1022 _mapcache_cache_google_headers_add(ctx, "DELETE", pcache, tile, url, headers);
1023 }
1024
1025
_mapcache_cache_rest_has_tile(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)1026 static int _mapcache_cache_rest_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
1027 {
1028 mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
1029 char *url;
1030 apr_table_t *headers;
1031 int status;
1032 mapcache_pooled_connection *pc;
1033 CURL *curl;
1034
1035 _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.has_tile, &url);
1036 headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.has_tile);
1037
1038 if(GC_HAS_ERROR(ctx))
1039 return MAPCACHE_FAILURE;
1040
1041 if(rcache->rest.add_headers) {
1042 rcache->rest.add_headers(ctx,rcache,tile,url,headers);
1043 }
1044 if(rcache->rest.has_tile.add_headers) {
1045 rcache->rest.has_tile.add_headers(ctx,rcache,tile,url,headers);
1046 }
1047
1048 pc = _rest_get_connection(ctx, rcache, tile);
1049 if(GC_HAS_ERROR(ctx))
1050 return MAPCACHE_FAILURE;
1051
1052 curl = pc->connection;
1053
1054 status = _head_request(ctx, curl, url, headers);
1055
1056
1057 if(GC_HAS_ERROR(ctx)) {
1058 mapcache_connection_pool_invalidate_connection(ctx,pc);
1059 return MAPCACHE_FAILURE;
1060 }
1061
1062 mapcache_connection_pool_release_connection(ctx,pc);
1063
1064 if( status == 200)
1065 return MAPCACHE_TRUE;
1066 else
1067 return MAPCACHE_FALSE;
1068 }
1069
_mapcache_cache_rest_delete(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)1070 static void _mapcache_cache_rest_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
1071 {
1072 mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
1073 char *url;
1074 apr_table_t *headers;
1075 int status;
1076 mapcache_pooled_connection *pc;
1077 CURL *curl;
1078 _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.delete_tile, &url);
1079 headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.delete_tile);
1080 GC_CHECK_ERROR(ctx);
1081
1082 if(rcache->rest.add_headers) {
1083 rcache->rest.add_headers(ctx,rcache,tile,url,headers);
1084 }
1085 if(rcache->rest.delete_tile.add_headers) {
1086 rcache->rest.delete_tile.add_headers(ctx,rcache,tile,url,headers);
1087 }
1088
1089 pc = _rest_get_connection(ctx, rcache, tile);
1090 GC_CHECK_ERROR(ctx);
1091
1092 curl = pc->connection;
1093
1094 status = _delete_request(ctx, curl, url, headers);
1095 if(GC_HAS_ERROR(ctx)) {
1096 mapcache_connection_pool_invalidate_connection(ctx,pc);
1097 return;
1098 }
1099 mapcache_connection_pool_release_connection(ctx,pc);
1100
1101 if(status!=200 && status!=202 && status!=204 && status!=404 && status!=410) {
1102 //ctx->set_error(ctx,500,"rest delete returned code %d", status);
1103 }
1104 }
1105
1106
1107 /**
1108 * \brief get file content of given tile
1109 *
1110 * fills the mapcache_tile::data of the given tile with content stored in the file
1111 * \private \memberof mapcache_cache_rest
1112 * \sa mapcache_cache::tile_get()
1113 */
_mapcache_cache_rest_get(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)1114 static int _mapcache_cache_rest_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
1115 {
1116 mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
1117 char *url;
1118 apr_table_t *headers;
1119 mapcache_pooled_connection *pc;
1120 CURL *curl;
1121 _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.get_tile, &url);
1122 if(tile->allow_redirect && rcache->use_redirects) {
1123 tile->redirect = url;
1124 return MAPCACHE_SUCCESS;
1125 }
1126 headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.get_tile);
1127
1128 if(GC_HAS_ERROR(ctx))
1129 return MAPCACHE_FAILURE;
1130
1131 if(rcache->rest.add_headers) {
1132 rcache->rest.add_headers(ctx,rcache,tile,url,headers);
1133 }
1134 if(rcache->rest.get_tile.add_headers) {
1135 rcache->rest.get_tile.add_headers(ctx,rcache,tile,url,headers);
1136 }
1137
1138 pc = _rest_get_connection(ctx, rcache, tile);
1139 if(GC_HAS_ERROR(ctx))
1140 return MAPCACHE_FAILURE;
1141
1142 curl = pc->connection;
1143
1144 tile->encoded_data = _get_request(ctx, curl, url, headers);
1145
1146 if(GC_HAS_ERROR(ctx)) {
1147 mapcache_connection_pool_invalidate_connection(ctx,pc);
1148 return MAPCACHE_FAILURE;
1149 }
1150 mapcache_connection_pool_release_connection(ctx,pc);
1151
1152 if(!tile->encoded_data)
1153 return MAPCACHE_CACHE_MISS;
1154
1155 return MAPCACHE_SUCCESS;
1156 }
1157
_mapcache_cache_rest_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)1158 static void _mapcache_cache_rest_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) {
1159 mapcache_cache_rest *rcache = (mapcache_cache_rest*)pcache;
1160 char *url;
1161 apr_table_t *headers;
1162 mapcache_pooled_connection *pc;
1163 CURL *curl;
1164
1165
1166 if(rcache->detect_blank) {
1167 if(tile->nodata) {
1168 return;
1169 }
1170 if(!tile->raw_image) {
1171 tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
1172 GC_CHECK_ERROR(ctx);
1173 }
1174 if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
1175 if(tile->raw_image->data[3] == 0) {
1176 /* We have a blank (uniform) image who's first pixel is fully transparent, thus the whole image is transparent */
1177 tile->nodata = 1;
1178 return;
1179 }
1180 }
1181 }
1182
1183 _mapcache_cache_rest_tile_url(ctx, tile, &rcache->rest, &rcache->rest.set_tile, &url);
1184 headers = _mapcache_cache_rest_headers(ctx, tile, &rcache->rest, &rcache->rest.set_tile);
1185 GC_CHECK_ERROR(ctx);
1186
1187 if(!tile->encoded_data) {
1188 tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
1189 GC_CHECK_ERROR(ctx);
1190 }
1191
1192 apr_table_set(headers,"Content-Length",apr_psprintf(ctx->pool,"%lu",tile->encoded_data->size));
1193 if(tile->tileset->format && tile->tileset->format->mime_type)
1194 apr_table_set(headers, "Content-Type", tile->tileset->format->mime_type);
1195 else {
1196 mapcache_image_format_type imgfmt = mapcache_imageio_header_sniff(ctx,tile->encoded_data);
1197 if(imgfmt == GC_JPEG) {
1198 apr_table_set(headers, "Content-Type", "image/jpeg");
1199 } else if (imgfmt == GC_PNG) {
1200 apr_table_set(headers, "Content-Type", "image/png");
1201 }
1202 }
1203
1204 if(rcache->rest.add_headers) {
1205 rcache->rest.add_headers(ctx,rcache,tile,url,headers);
1206 }
1207 if(rcache->rest.set_tile.add_headers) {
1208 rcache->rest.set_tile.add_headers(ctx,rcache,tile,url,headers);
1209 }
1210
1211 pc = _rest_get_connection(ctx, rcache, tile);
1212 GC_CHECK_ERROR(ctx);
1213 curl = pc->connection;
1214
1215 _put_request(ctx, curl, tile->encoded_data, url, headers);
1216 if(GC_HAS_ERROR(ctx)) {
1217 mapcache_connection_pool_invalidate_connection(ctx,pc);
1218 return;
1219 }
1220
1221 mapcache_connection_pool_release_connection(ctx,pc);
1222 }
1223
_mapcache_cache_rest_operation_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * cache,mapcache_rest_operation * op)1224 static void _mapcache_cache_rest_operation_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_rest_operation *op)
1225 {
1226 ezxml_t cur_node;
1227 if ((cur_node = ezxml_child(node,"headers")) != NULL) {
1228 ezxml_t header_node;
1229 op->headers = apr_table_make(ctx->pool,3);
1230 for(header_node = cur_node->child; header_node; header_node = header_node->sibling) {
1231 apr_table_set(op->headers, header_node->name, header_node->txt);
1232 }
1233 }
1234 if ((cur_node = ezxml_child(node,"header_file")) != NULL) {
1235 op->header_file = apr_pstrdup(ctx->pool, cur_node->txt);
1236 }
1237 }
1238
1239 /**
1240 * \private \memberof mapcache_cache_rest
1241 */
_mapcache_cache_rest_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * cache,mapcache_cfg * config)1242 static void _mapcache_cache_rest_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
1243 {
1244 ezxml_t cur_node;
1245 mapcache_cache_rest *dcache = (mapcache_cache_rest*)cache;
1246 if ((cur_node = ezxml_child(node,"url")) != NULL) {
1247 dcache->rest.tile_url = apr_pstrdup(ctx->pool,cur_node->txt);
1248 }
1249 if ((cur_node = ezxml_child(node,"use_redirects")) != NULL) {
1250 if(!strcasecmp(cur_node->txt,"true")) {
1251 dcache->use_redirects = 1;
1252 }
1253 }
1254 if ((cur_node = ezxml_child(node,"connection_timeout")) != NULL) {
1255 char *endptr;
1256 dcache->connection_timeout = (int)strtol(cur_node->txt,&endptr,10);
1257 if(*endptr != 0 || dcache->connection_timeout<1) {
1258 ctx->set_error(ctx,400,"invalid rest cache <connection_timeout> \"%s\" (positive integer expected)",
1259 cur_node->txt);
1260 return;
1261 }
1262 } else {
1263 dcache->connection_timeout = 30;
1264 }
1265
1266 if ((cur_node = ezxml_child(node,"timeout")) != NULL) {
1267 char *endptr;
1268 dcache->timeout = (int)strtol(cur_node->txt,&endptr,10);
1269 if(*endptr != 0 || dcache->timeout<1) {
1270 ctx->set_error(ctx,400,"invalid rest cache <timeout> \"%s\" (positive integer expected)",
1271 cur_node->txt);
1272 return;
1273 }
1274 } else {
1275 dcache->timeout = 120;
1276 }
1277
1278 dcache->detect_blank = 0;
1279 if ((cur_node = ezxml_child(node, "detect_blank")) != NULL) {
1280 if(strcasecmp(cur_node->txt,"false")) {
1281 dcache->detect_blank = 1;
1282 }
1283 }
1284
1285 if ((cur_node = ezxml_child(node,"headers")) != NULL) {
1286 ezxml_t header_node;
1287 dcache->rest.common_headers = apr_table_make(ctx->pool,3);
1288 for(header_node = cur_node->child; header_node; header_node = header_node->sibling) {
1289 apr_table_set(dcache->rest.common_headers, header_node->name, header_node->txt);
1290 }
1291 }
1292
1293 if ((cur_node = ezxml_child(node,"header_file")) != NULL) {
1294 dcache->rest.header_file = apr_pstrdup(ctx->pool, cur_node->txt);
1295 }
1296
1297
1298 for(cur_node = ezxml_child(node,"operation"); cur_node; cur_node = cur_node->next) {
1299 char *type = (char*)ezxml_attr(cur_node,"type");
1300 if(!type) {
1301 ctx->set_error(ctx,400,"<operation> with no \"type\" attribute in cache (%s)", cache->name);
1302 return;
1303 }
1304 if(!strcasecmp(type,"put")) {
1305 _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.set_tile);
1306 GC_CHECK_ERROR(ctx);
1307 } else if(!strcasecmp(type,"get")) {
1308 _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.get_tile);
1309 GC_CHECK_ERROR(ctx);
1310 } else if(!strcasecmp(type,"head")) {
1311 _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.has_tile);
1312 GC_CHECK_ERROR(ctx);
1313 } else if(!strcasecmp(type,"delete")) {
1314 _mapcache_cache_rest_operation_parse_xml(ctx,cur_node,cache,&dcache->rest.delete_tile);
1315 GC_CHECK_ERROR(ctx);
1316 } else {
1317 ctx->set_error(ctx,400,"<operation> with unknown \"type\" (%s) attribute in cache (%s) (expecting put, get, head or delete)", type, cache->name);
1318 return;
1319 }
1320 }
1321
1322 }
1323
_mapcache_cache_google_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * cache,mapcache_cfg * config)1324 static void _mapcache_cache_google_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
1325 {
1326 ezxml_t cur_node;
1327 mapcache_cache_google *google = (mapcache_cache_google*)cache;
1328 _mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config);
1329 GC_CHECK_ERROR(ctx);
1330 if ((cur_node = ezxml_child(node,"access")) != NULL) {
1331 google->access = apr_pstrdup(ctx->pool, cur_node->txt);
1332 } else {
1333 ctx->set_error(ctx,400,"google cache (%s) is missing required <access> child", cache->name);
1334 return;
1335 }
1336 if ((cur_node = ezxml_child(node,"secret")) != NULL) {
1337 google->secret = apr_pstrdup(ctx->pool, cur_node->txt);
1338 } else {
1339 ctx->set_error(ctx,400,"google cache (%s) is missing required <secret> child", cache->name);
1340 return;
1341 }
1342 }
1343
_mapcache_cache_s3_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * cache,mapcache_cfg * config)1344 static void _mapcache_cache_s3_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
1345 {
1346 ezxml_t cur_node;
1347 mapcache_cache_s3 *s3 = (mapcache_cache_s3*)cache;
1348 _mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config);
1349 GC_CHECK_ERROR(ctx);
1350 if ((cur_node = ezxml_child(node,"credentials_file")) != NULL) {
1351 s3->credentials_file = apr_pstrdup(ctx->pool, cur_node->txt);
1352 } else {
1353 if ((cur_node = ezxml_child(node,"id")) != NULL) {
1354 s3->id = apr_pstrdup(ctx->pool, cur_node->txt);
1355 } else if ( getenv("AWS_ACCESS_KEY_ID")) {
1356 s3->id = apr_pstrdup(ctx->pool,getenv("AWS_ACCESS_KEY_ID"));
1357 } else {
1358 ctx->set_error(ctx,400,"s3 cache (%s) is missing required <id> child or AWS_ACCESS_KEY_ID environment", cache->name);
1359 return;
1360 }
1361 if ((cur_node = ezxml_child(node,"secret")) != NULL) {
1362 s3->secret = apr_pstrdup(ctx->pool, cur_node->txt);
1363 } else if ( getenv("AWS_SECRET_ACCESS_KEY")) {
1364 s3->secret = apr_pstrdup(ctx->pool,getenv("AWS_SECRET_ACCESS_KEY"));
1365 } else {
1366 ctx->set_error(ctx,400,"s3 cache (%s) is missing required <secret> child or AWS_SECRET_ACCESS_KEY environment", cache->name);
1367 return;
1368 }
1369 }
1370 if ((cur_node = ezxml_child(node,"region")) != NULL) {
1371 s3->region = apr_pstrdup(ctx->pool, cur_node->txt);
1372 } else {
1373 ctx->set_error(ctx,400,"s3 cache (%s) is missing required <region> child", cache->name);
1374 return;
1375 }
1376 }
1377
_mapcache_cache_azure_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * cache,mapcache_cfg * config)1378 static void _mapcache_cache_azure_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *cache, mapcache_cfg *config)
1379 {
1380 ezxml_t cur_node;
1381 mapcache_cache_azure *azure = (mapcache_cache_azure*)cache;
1382 _mapcache_cache_rest_configuration_parse_xml(ctx, node, cache, config);
1383 GC_CHECK_ERROR(ctx);
1384 if ((cur_node = ezxml_child(node,"id")) != NULL) {
1385 azure->id = apr_pstrdup(ctx->pool, cur_node->txt);
1386 } else {
1387 ctx->set_error(ctx,400,"azure cache (%s) is missing required <id> child", cache->name);
1388 return;
1389 }
1390 if ((cur_node = ezxml_child(node,"secret")) != NULL) {
1391 azure->secret = apr_pstrdup(ctx->pool, cur_node->txt);
1392 } else {
1393 ctx->set_error(ctx,400,"azure cache (%s) is missing required <secret> child", cache->name);
1394 return;
1395 }
1396 if ((cur_node = ezxml_child(node,"container")) != NULL) {
1397 azure->container = apr_pstrdup(ctx->pool, cur_node->txt);
1398 } else {
1399 ctx->set_error(ctx,400,"azure cache (%s) is missing required <container> child", cache->name);
1400 return;
1401 }
1402 }
1403
1404 /**
1405 * \private \memberof mapcache_cache_rest
1406 */
_mapcache_cache_rest_configuration_post_config(mapcache_context * ctx,mapcache_cache * cache,mapcache_cfg * cfg)1407 static void _mapcache_cache_rest_configuration_post_config(mapcache_context *ctx, mapcache_cache *cache,
1408 mapcache_cfg *cfg)
1409 {
1410 mapcache_cache_rest *dcache = (mapcache_cache_rest*)cache;
1411
1412
1413 if(!dcache->rest.tile_url) {
1414 if(!dcache->rest.delete_tile.tile_url) {
1415 ctx->set_error(ctx,400, "rest cache (%s) has no global <url> and no <url> for delete_tile operation", cache->name);
1416 return;
1417 }
1418 if(!dcache->rest.get_tile.tile_url) {
1419 ctx->set_error(ctx,400, "rest cache (%s) has no global <url> and no <url> for get_tile operation", cache->name);
1420 return;
1421 }
1422 if(!dcache->rest.set_tile.tile_url) {
1423 ctx->set_error(ctx,400, "rest cache (%s) has no global <url> and no <url> for set_tile operation", cache->name);
1424 return;
1425 }
1426 }
1427 }
1428
mapcache_cache_rest_init(mapcache_context * ctx,mapcache_cache_rest * cache)1429 void mapcache_cache_rest_init(mapcache_context *ctx, mapcache_cache_rest *cache) {
1430 cache->use_redirects = 0;
1431 cache->rest.get_tile.method = MAPCACHE_REST_METHOD_GET;
1432 cache->rest.set_tile.method = MAPCACHE_REST_METHOD_PUT;
1433 cache->rest.delete_tile.method = MAPCACHE_REST_METHOD_DELETE;
1434 cache->rest.multi_set_tile.method = MAPCACHE_REST_METHOD_PUT;
1435 cache->rest.has_tile.method = MAPCACHE_REST_METHOD_HEAD;
1436 cache->cache.metadata = apr_table_make(ctx->pool,3);
1437 cache->cache.type = MAPCACHE_CACHE_REST;
1438 cache->cache._tile_delete = _mapcache_cache_rest_delete;
1439 cache->cache._tile_get = _mapcache_cache_rest_get;
1440 cache->cache._tile_exists = _mapcache_cache_rest_has_tile;
1441 cache->cache._tile_set = _mapcache_cache_rest_set;
1442 cache->cache.configuration_post_config = _mapcache_cache_rest_configuration_post_config;
1443 cache->cache.configuration_parse_xml = _mapcache_cache_rest_configuration_parse_xml;
1444 }
1445 /**
1446 * \brief creates and initializes a mapcache_rest_cache
1447 */
mapcache_cache_rest_create(mapcache_context * ctx)1448 mapcache_cache* mapcache_cache_rest_create(mapcache_context *ctx)
1449 {
1450 mapcache_cache_rest *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_rest));
1451 if(!cache) {
1452 ctx->set_error(ctx, 500, "failed to allocate rest cache");
1453 return NULL;
1454 }
1455 mapcache_cache_rest_init(ctx,cache);
1456 cache->provider = MAPCACHE_REST_PROVIDER_NONE;
1457 return (mapcache_cache*)cache;
1458 }
1459
1460 /**
1461 * \brief creates and initializes a mapcache_s3_cache
1462 */
mapcache_cache_s3_create(mapcache_context * ctx)1463 mapcache_cache* mapcache_cache_s3_create(mapcache_context *ctx)
1464 {
1465 mapcache_cache_s3 *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_s3));
1466 if(!cache) {
1467 ctx->set_error(ctx, 500, "failed to allocate s3 cache");
1468 return NULL;
1469 }
1470 mapcache_cache_rest_init(ctx,&cache->cache);
1471 cache->cache.provider = MAPCACHE_REST_PROVIDER_S3;
1472 cache->cache.cache.configuration_parse_xml = _mapcache_cache_s3_configuration_parse_xml;
1473 cache->cache.rest.get_tile.add_headers = _mapcache_cache_s3_get_headers_add;
1474 cache->cache.rest.has_tile.add_headers = _mapcache_cache_s3_head_headers_add;
1475 cache->cache.rest.set_tile.add_headers = _mapcache_cache_s3_put_headers_add;
1476 cache->cache.rest.delete_tile.add_headers = _mapcache_cache_s3_delete_headers_add;
1477 return (mapcache_cache*)cache;
1478 }
1479
1480 /**
1481 * \brief creates and initializes a mapcache_azure_cache
1482 */
mapcache_cache_azure_create(mapcache_context * ctx)1483 mapcache_cache* mapcache_cache_azure_create(mapcache_context *ctx)
1484 {
1485 mapcache_cache_azure *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_azure));
1486 if(!cache) {
1487 ctx->set_error(ctx, 500, "failed to allocate azure cache");
1488 return NULL;
1489 }
1490 mapcache_cache_rest_init(ctx,&cache->cache);
1491 cache->cache.provider = MAPCACHE_REST_PROVIDER_AZURE;
1492 cache->cache.cache.configuration_parse_xml = _mapcache_cache_azure_configuration_parse_xml;
1493 cache->cache.rest.get_tile.add_headers = _mapcache_cache_azure_get_headers_add;
1494 cache->cache.rest.has_tile.add_headers = _mapcache_cache_azure_head_headers_add;
1495 cache->cache.rest.set_tile.add_headers = _mapcache_cache_azure_put_headers_add;
1496 cache->cache.rest.delete_tile.add_headers = _mapcache_cache_azure_delete_headers_add;
1497 return (mapcache_cache*)cache;
1498 }
1499
1500 /**
1501 * \brief creates and initializes a mapcache_google_cache
1502 */
mapcache_cache_google_create(mapcache_context * ctx)1503 mapcache_cache* mapcache_cache_google_create(mapcache_context *ctx)
1504 {
1505 mapcache_cache_google *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_google));
1506 if(!cache) {
1507 ctx->set_error(ctx, 500, "failed to allocate google cache");
1508 return NULL;
1509 }
1510 mapcache_cache_rest_init(ctx,&cache->cache);
1511 cache->cache.provider = MAPCACHE_REST_PROVIDER_GOOGLE;
1512 cache->cache.cache.configuration_parse_xml = _mapcache_cache_google_configuration_parse_xml;
1513 cache->cache.rest.get_tile.add_headers = _mapcache_cache_google_get_headers_add;
1514 cache->cache.rest.has_tile.add_headers = _mapcache_cache_google_head_headers_add;
1515 cache->cache.rest.set_tile.add_headers = _mapcache_cache_google_put_headers_add;
1516 cache->cache.rest.delete_tile.add_headers = _mapcache_cache_google_delete_headers_add;
1517 return (mapcache_cache*)cache;
1518 }
1519
1520
1521
1522 /* vim: ts=2 sts=2 et sw=2
1523 */
1524