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, &params);
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