1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  MapCache tile caching: tiled tiff filesytem cache backend.
6  * Author:   Thomas Bonfort
7  *           Frank Warmerdam
8  *           Even Rouault
9  *           MapServer team.
10  *
11  ******************************************************************************
12  * Copyright (c) 2011-2017 Regents of the University of Minnesota.
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice shall be included in
22  * all copies of this Software or works derived from this Software.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30  * DEALINGS IN THE SOFTWARE.
31  *****************************************************************************/
32 
33 #include "mapcache.h"
34 #ifdef USE_TIFF
35 
36 #include <apr_file_info.h>
37 #include <apr_strings.h>
38 #include <apr_file_io.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <tiffio.h>
43 
44 #ifdef USE_GDAL
45 #include "cpl_vsi.h"
46 #include "cpl_conv.h"
47 #define CPL_SERV_H_INCLUDED
48 #endif
49 
50 #ifdef USE_GEOTIFF
51 #include "xtiffio.h"
52 #include "geovalues.h"
53 #include "geotiff.h"
54 #include "geo_normalize.h"
55 #include "geo_tiffp.h"
56 #include "geo_keyp.h"
57 #define MyTIFFOpen XTIFFOpen
58 #define MyTIFFClose XTIFFClose
59 #else
60 #define MyTIFFOpen TIFFOpen
61 #define MyTIFFClose TIFFClose
62 #endif
63 
64 typedef enum
65 {
66     MAPCACHE_TIFF_STORAGE_FILE,
67     MAPCACHE_TIFF_STORAGE_REST,
68     MAPCACHE_TIFF_STORAGE_GOOGLE
69 } mapcache_cache_tiff_storage_type;
70 
71 
72 typedef struct mapcache_cache_tiff mapcache_cache_tiff;
73 
74 struct mapcache_cache_tiff {
75   mapcache_cache cache;
76   char *filename_template;
77   char *x_fmt,*y_fmt,*z_fmt,*inv_x_fmt,*inv_y_fmt,*div_x_fmt,*div_y_fmt,*inv_div_x_fmt,*inv_div_y_fmt;
78   int count_x;
79   int count_y;
80   mapcache_image_format_jpeg *format;
81   mapcache_locker *locker;
82   struct {
83     mapcache_cache_tiff_storage_type type;
84     int connection_timeout;
85     int timeout;
86     char *header_file;
87     union
88     {
89         struct
90         {
91             char* access;
92             char* secret;
93         } google;
94     } u;
95   } storage;
96 };
97 
98 #ifdef USE_GDAL
99 
100 static tsize_t
_tiffReadProc(thandle_t th,tdata_t buf,tsize_t size)101 _tiffReadProc( thandle_t th, tdata_t buf, tsize_t size )
102 {
103     VSILFILE* fp = (VSILFILE*)th;
104     return VSIFReadL( buf, 1, size, fp );
105 }
106 
107 static tsize_t
_tiffWriteProc(thandle_t th,tdata_t buf,tsize_t size)108 _tiffWriteProc( thandle_t th, tdata_t buf, tsize_t size )
109 {
110     VSILFILE* fp = (VSILFILE*)th;
111     return VSIFWriteL( buf, 1, size, fp );
112 }
113 
114 static toff_t
_tiffSeekProc(thandle_t th,toff_t off,int whence)115 _tiffSeekProc( thandle_t th, toff_t off, int whence )
116 {
117     VSILFILE* fp = (VSILFILE*)th;
118     if( VSIFSeekL( fp, off, whence ) != 0 )
119     {
120         TIFFErrorExt( th, "_tiffSeekProc", "%s", VSIStrerror( errno ) );
121         return (toff_t)( -1 );
122     }
123     return VSIFTellL( fp );
124 }
125 
126 static int
_tiffCloseProc(thandle_t th)127 _tiffCloseProc( thandle_t th )
128 {
129     VSILFILE* fp = (VSILFILE*)th;
130     VSIFCloseL(fp);
131     return 0;
132 }
133 
134 static toff_t
_tiffSizeProc(thandle_t th)135 _tiffSizeProc( thandle_t th )
136 {
137     vsi_l_offset old_off;
138     toff_t file_size;
139     VSILFILE* fp = (VSILFILE*)th;
140 
141     old_off = VSIFTellL( fp );
142     (void)VSIFSeekL( fp, 0, SEEK_END );
143 
144     file_size = (toff_t) VSIFTellL( fp );
145     (void)VSIFSeekL( fp, old_off, SEEK_SET );
146 
147     return file_size;
148 }
149 
150 static int
_tiffMapProc(thandle_t th,tdata_t * pbase,toff_t * psize)151 _tiffMapProc( thandle_t th, tdata_t* pbase, toff_t* psize )
152 {
153     (void)th;
154     (void)pbase;
155     (void)psize;
156     /* Unimplemented */
157     return 0;
158 }
159 
160 static void
_tiffUnmapProc(thandle_t th,tdata_t base,toff_t size)161 _tiffUnmapProc( thandle_t th, tdata_t base, toff_t size )
162 {
163     (void)th;
164     (void)base;
165     (void)size;
166     /* Unimplemented */
167 }
168 
set_conf_value(const char * key,const char * value)169 static char* set_conf_value(const char* key, const char* value)
170 {
171     const char* old_val_const;
172     char* old_val = NULL;
173     old_val_const = CPLGetConfigOption(key, NULL);
174     if( old_val_const != NULL )
175         old_val = strdup(old_val_const);
176     /* Prevent a directory listing to be done */
177     CPLSetConfigOption(key, value);
178     return old_val;
179 }
180 
restore_conf_value(const char * key,char * old_val)181 static void restore_conf_value(const char* key, char* old_val)
182 {
183     CPLSetConfigOption(key, old_val);
184     free(old_val);
185 }
186 
187 typedef struct
188 {
189     char* old_val_disable_readdir;
190     char* old_val_headerfile;
191     char* old_val_secret;
192     char* old_val_access;
193 } mapache_gdal_env_context;
194 
set_gdal_context(mapcache_cache_tiff * cache,mapache_gdal_env_context * pcontext)195 static void set_gdal_context(mapcache_cache_tiff *cache,
196                              mapache_gdal_env_context* pcontext)
197 {
198     memset(pcontext, 0, sizeof(mapache_gdal_env_context));
199     /* Prevent a directory listing to be done */
200     pcontext->old_val_disable_readdir =
201         set_conf_value("GDAL_DISABLE_READDIR_ON_OPEN", "YES");
202 
203     if( cache->storage.header_file ) {
204         pcontext->old_val_headerfile = set_conf_value("GDAL_HTTP_HEADER_FILE",
205                                             cache->storage.header_file);
206     }
207     if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE ) {
208         pcontext->old_val_secret = set_conf_value("GS_SECRET_ACCESS_KEY",
209                                         cache->storage.u.google.secret);
210         pcontext->old_val_access = set_conf_value("GS_ACCESS_KEY_ID",
211                                         cache->storage.u.google.access);
212     }
213 }
214 
restore_gdal_context(mapcache_cache_tiff * cache,mapache_gdal_env_context * pcontext)215 static void restore_gdal_context(mapcache_cache_tiff *cache,
216                                  mapache_gdal_env_context* pcontext)
217 {
218 
219     restore_conf_value("GDAL_DISABLE_READDIR_ON_OPEN",
220                        pcontext->old_val_disable_readdir);
221     if( cache->storage.header_file ) {
222         restore_conf_value("GDAL_HTTP_HEADER_FILE",
223                            pcontext->old_val_headerfile);
224     }
225     if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE ) {
226         restore_conf_value("GS_SECRET_ACCESS_KEY",
227                            pcontext->old_val_secret);
228         restore_conf_value("GS_ACCESS_KEY_ID",
229                            pcontext->old_val_access);
230     }
231 }
232 
mapcache_cache_tiff_vsi_stat(mapcache_cache_tiff * cache,const char * name,VSIStatBufL * pstat)233 static int mapcache_cache_tiff_vsi_stat(
234                                       mapcache_cache_tiff *cache,
235                                       const char* name,
236                                       VSIStatBufL* pstat)
237 {
238     mapache_gdal_env_context context;
239     int ret;
240 
241     set_gdal_context(cache, &context);
242     ret = VSIStatL(name, pstat);
243     restore_gdal_context(cache, &context);
244 
245     return ret;
246 }
247 
mapcache_cache_tiff_vsi_open(mapcache_cache_tiff * cache,const char * name,const char * mode)248 static VSILFILE* mapcache_cache_tiff_vsi_open(
249                                       mapcache_cache_tiff *cache,
250                                       const char* name, const char* mode )
251 {
252     mapache_gdal_env_context context;
253     VSILFILE* fp;
254 
255     set_gdal_context(cache, &context);
256     fp = VSIFOpenL(name, mode);
257     restore_gdal_context(cache, &context);
258 
259     return fp;
260 }
261 
mapcache_cache_tiff_open(mapcache_context * ctx,mapcache_cache_tiff * cache,const char * name,const char * mode)262 static TIFF* mapcache_cache_tiff_open(mapcache_context *ctx,
263                                       mapcache_cache_tiff *cache,
264                                       const char* name, const char* mode )
265 {
266     char chDummy;
267     VSILFILE* fp;
268 
269     /* If writing or using a regular filename, then use standard */
270     /* libtiff/libgeotiff I/O layer */
271     if( strcmp(mode, "r") != 0 || strncmp(name, "/vsi", 4) != 0 )
272     {
273         return MyTIFFOpen(name, mode);
274     }
275 
276     fp = mapcache_cache_tiff_vsi_open(cache, name, mode);
277     if( fp == NULL )
278         return NULL;
279 
280     if( strcmp(mode, "r") == 0 )
281     {
282         /* But then the file descriptor may point to an invalid resource */
283         /* so try reading a byte from it */
284         if(VSIFReadL(&chDummy, 1, 1, fp) != 1)
285         {
286             VSIFCloseL(fp);
287             return NULL;
288         }
289         VSIFSeekL(fp, 0, SEEK_SET);
290     }
291 
292     return
293 #ifdef USE_GEOTIFF
294         XTIFFClientOpen
295 #else
296         TIFFClientOpen
297 #endif
298                         ( name, mode,
299                          (thandle_t) fp,
300                          _tiffReadProc, _tiffWriteProc,
301                          _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
302                          _tiffMapProc, _tiffUnmapProc );
303 }
304 
mapcache_cache_tiff_gdal_error_handler(CPLErr eErr,int error_num,const char * pszMsg)305 static void CPL_STDCALL mapcache_cache_tiff_gdal_error_handler(CPLErr eErr,
306                                                    int error_num,
307                                                    const char* pszMsg)
308 {
309 #ifdef DEBUG
310     mapcache_context *ctx = (mapcache_context *) CPLGetErrorHandlerUserData();
311     ctx->log(ctx,MAPCACHE_DEBUG,"GDAL %s, %d: %s",
312              (eErr == CE_Failure) ? "Failure":
313              (eErr == CE_Warning) ? "Warning":
314                                     "Debug",
315              error_num,
316              pszMsg);
317 #endif
318 }
319 
320 
321 #else
322 
mapcache_cache_tiff_open(mapcache_context * ctx,mapcache_cache_tiff * cache,const char * name,const char * mode)323 static TIFF* mapcache_cache_tiff_open(mapcache_context *ctx,
324                                       mapcache_cache_tiff *cache,
325                                       const char* name, const char* mode )
326 {
327     (void)ctx;
328     (void)cache;
329     return MyTIFFOpen(name, mode);
330 
331 }
332 
333 #endif /* USE_GDAL */
334 
335 #undef MyTIFFOpen
336 
337 /**
338  * \brief return filename for given tile
339  *
340  * \param tile the tile to get the key from
341  * \param path pointer to a char* that will contain the filename
342  * \param r
343  * \private \memberof mapcache_cache_tiff
344  */
_mapcache_cache_tiff_tile_key(mapcache_context * ctx,mapcache_cache_tiff * cache,mapcache_tile * tile,char ** path)345 static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, char **path)
346 {
347   if( cache->storage.type == MAPCACHE_TIFF_STORAGE_REST ) {
348     *path = apr_pstrcat(ctx->pool, "/vsicurl/", cache->filename_template, NULL);
349   }
350   else if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE &&
351            strncmp(cache->filename_template,
352                    "https://storage.googleapis.com/",
353                    strlen("https://storage.googleapis.com/")) == 0) {
354     *path = apr_pstrcat(ctx->pool, "/vsigs/",
355         cache->filename_template +
356             strlen("https://storage.googleapis.com/"), NULL);
357   }
358   else {
359     *path = apr_pstrdup(ctx->pool, cache->filename_template);
360   }
361 
362   /*
363    * generic template substitutions
364    */
365   if(strstr(*path,"{tileset}"))
366     *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}",
367                                       tile->tileset->name);
368   if(strstr(*path,"{grid}"))
369     *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}",
370                                       tile->grid_link->grid->name);
371   if(tile->dimensions && strstr(*path,"{dim")) {
372     char *dimstring="";
373     int i = tile->dimensions->nelts;
374     while(i--) {
375       mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
376       const char *dimval = mapcache_util_str_sanitize(ctx->pool,rdim->cached_value,"/.",'#');
377       char *dim_key = apr_pstrcat(ctx->pool,"{dim:",rdim->dimension->name,"}",NULL);
378       dimstring = apr_pstrcat(ctx->pool,dimstring,"#",dimval,NULL);
379       if(strstr(*path,dim_key)) {
380         *path = mapcache_util_str_replace(ctx->pool,*path, dim_key, dimval);
381       }
382     }
383     *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
384   }
385 
386 
387   while(strstr(*path,"{z}"))
388     *path = mapcache_util_str_replace(ctx->pool,*path, "{z}",
389                                       apr_psprintf(ctx->pool,cache->z_fmt,tile->z));
390   /*
391    * x and y replacing, when the tiff files are numbered with an increasing
392    * x,y scheme (adjacent tiffs have x-x'=1 or y-y'=1
393    */
394   while(strstr(*path,"{div_x}"))
395     *path = mapcache_util_str_replace(ctx->pool,*path, "{div_x}",
396                                       apr_psprintf(ctx->pool,cache->div_x_fmt,tile->x/cache->count_x));
397   while(strstr(*path,"{div_y}"))
398     *path = mapcache_util_str_replace(ctx->pool,*path, "{div_y}",
399                                       apr_psprintf(ctx->pool,cache->div_y_fmt,tile->y/cache->count_y));
400   while(strstr(*path,"{inv_div_y}"))
401     *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_y}",
402                                       apr_psprintf(ctx->pool,cache->inv_div_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/cache->count_y));
403   while(strstr(*path,"{inv_div_x}"))
404     *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_x}",
405                                       apr_psprintf(ctx->pool,cache->inv_div_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/cache->count_x));
406 
407   /*
408    * x and y replacing, when the tiff files are numbered with the index
409    * of their bottom-left tile
410    * adjacent tiffs have x-x'=count_x or y-y'=count_y
411    */
412   while(strstr(*path,"{x}"))
413     *path = mapcache_util_str_replace(ctx->pool,*path, "{x}",
414                                       apr_psprintf(ctx->pool,cache->x_fmt,tile->x/cache->count_x*cache->count_x));
415   while(strstr(*path,"{y}"))
416     *path = mapcache_util_str_replace(ctx->pool,*path, "{y}",
417                                       apr_psprintf(ctx->pool,cache->y_fmt,tile->y/cache->count_y*cache->count_y));
418   while(strstr(*path,"{inv_y}"))
419     *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}",
420                                       apr_psprintf(ctx->pool,cache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/cache->count_y*cache->count_y));
421   while(strstr(*path,"{inv_x}"))
422     *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}",
423                                       apr_psprintf(ctx->pool,cache->inv_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/cache->count_x*cache->count_y));
424   if(!*path) {
425     ctx->set_error(ctx,500, "failed to allocate tile key");
426   }
427 }
428 
429 #ifdef DEBUG
check_tiff_format(mapcache_context * ctx,mapcache_cache_tiff * cache,mapcache_tile * tile,TIFF * hTIFF,const char * filename)430 static void check_tiff_format(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, TIFF *hTIFF, const char *filename)
431 {
432   uint32 imwidth,imheight,tilewidth,tileheight;
433   int16 planarconfig,orientation;
434   uint16 compression;
435   uint16 photometric;
436   int rv;
437   mapcache_grid_level *level;
438   int ntilesx;
439   int ntilesy;
440   TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &imwidth );
441   TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &imheight );
442   TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &tilewidth );
443   TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &tileheight );
444 
445   /* Test that the TIFF is tiled and not stripped */
446   if(!TIFFIsTiled(hTIFF)) {
447     ctx->set_error(ctx,500,"TIFF file \"%s\" is not tiled", filename);
448     return;
449   }
450 
451   /* check we have jpeg compression */
452   rv = TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &compression );
453   if(rv == 1 && compression != COMPRESSION_JPEG) {
454     ctx->set_error(ctx,500,"TIFF file \"%s\" is not jpeg compressed",
455                    filename);
456     return;
457   }
458 
459   /* tiff must be pixel interleaved, not with a single image per band */
460   rv = TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &planarconfig );
461   if(rv == 1 && planarconfig != PLANARCONFIG_CONTIG) {
462     ctx->set_error(ctx,500,"TIFF file \"%s\" is not pixel interleaved",
463                    filename);
464     return;
465   }
466 
467   /* is this test needed once we now we have JPEG ? */
468   rv = TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &photometric );
469   if(rv == 1 && (photometric != PHOTOMETRIC_RGB && photometric != PHOTOMETRIC_YCBCR)) {
470     ctx->set_error(ctx,500,"TIFF file \"%s\" is not RGB: %d",
471                    filename);
472     return;
473   }
474 
475   /* the default is top-left, but check just in case */
476   rv = TIFFGetField( hTIFF, TIFFTAG_ORIENTATION, &orientation );
477   if(rv == 1 && orientation != ORIENTATION_TOPLEFT) {
478     ctx->set_error(ctx,500,"TIFF file \"%s\" is not top-left oriented",
479                    filename);
480     return;
481   }
482 
483   /* check that the tiff internal tiling aligns with the mapcache_grid we are using:
484    * - the tiff tile size must match the grid tile size
485    * - the number of tiles in each direction in the tiff must match what has been
486    *   configured for the cache
487    */
488   level = tile->grid_link->grid->levels[tile->z];
489   ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
490   ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
491   if( tilewidth != tile->grid_link->grid->tile_sx ||
492       tileheight != tile->grid_link->grid->tile_sy ||
493       imwidth != tile->grid_link->grid->tile_sx * ntilesx ||
494       imheight != tile->grid_link->grid->tile_sy * ntilesy ) {
495     ctx->set_error(ctx,500,"TIFF file %s imagesize (%d,%d) and tilesize (%d,%d).\
496             Expected (%d,%d),(%d,%d)",filename,imwidth,imheight,tilewidth,tileheight,
497                    tile->grid_link->grid->tile_sx * ntilesx,
498                    tile->grid_link->grid->tile_sy * ntilesy,
499                    tile->grid_link->grid->tile_sx,
500                    tile->grid_link->grid->tile_sy);
501     return;
502   }
503 
504   /* TODO: more tests ? */
505 
506   /* ok, success */
507 }
508 #endif
509 
_mapcache_cache_tiff_has_tile(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)510 static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
511 {
512   char *filename;
513   TIFF *hTIFF;
514   mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
515   _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
516   if(GC_HAS_ERROR(ctx)) {
517     return MAPCACHE_FALSE;
518   }
519 
520 #ifdef USE_GDAL
521   CPLPushErrorHandlerEx(mapcache_cache_tiff_gdal_error_handler, ctx);
522 #endif
523 
524   hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r");
525 
526   if(hTIFF) {
527     do {
528       uint32 nSubType = 0;
529       int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */
530       int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */
531 
532       mapcache_grid_level *level;
533       int ntilesx;
534       int ntilesy;
535       toff_t  *offsets=NULL, *sizes=NULL;
536 
537       if( !TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType) )
538         nSubType = 0;
539 
540       /* skip overviews and masks */
541       if( (nSubType & FILETYPE_REDUCEDIMAGE) ||
542           (nSubType & FILETYPE_MASK) )
543         continue;
544 
545 
546 #ifdef DEBUG
547       check_tiff_format(ctx,cache,tile,hTIFF,filename);
548       if(GC_HAS_ERROR(ctx)) {
549         MyTIFFClose(hTIFF);
550 #ifdef USE_GDAL
551         CPLPopErrorHandler();
552 #endif
553         return MAPCACHE_FALSE;
554       }
555 #endif
556       level = tile->grid_link->grid->levels[tile->z];
557       ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
558       ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
559 
560       /* x offset of the tile along a row */
561       tiff_offx = tile->x % ntilesx;
562 
563       /*
564        * y offset of the requested row. we inverse it as the rows are ordered
565        * from top to bottom, whereas the tile y is bottom to top
566        */
567       tiff_offy = ntilesy - (tile->y % ntilesy) -1;
568       tiff_off = tiff_offy * ntilesx + tiff_offx;
569 
570       if(1 != TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &offsets )) {
571         MyTIFFClose(hTIFF);
572 #ifdef USE_GDAL
573         CPLPopErrorHandler();
574 #endif
575         return MAPCACHE_FALSE;
576       }
577       if(1 != TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &sizes )) {
578         MyTIFFClose(hTIFF);
579 #ifdef USE_GDAL
580         CPLPopErrorHandler();
581 #endif
582         return MAPCACHE_FALSE;
583       }
584       MyTIFFClose(hTIFF);
585       if( offsets[tiff_off] > 0 && sizes[tiff_off] > 0 ) {
586 #ifdef USE_GDAL
587         CPLPopErrorHandler();
588 #endif
589         return MAPCACHE_TRUE;
590       } else {
591 #ifdef USE_GDAL
592         CPLPopErrorHandler();
593 #endif
594         return MAPCACHE_FALSE;
595       }
596     } while( TIFFReadDirectory( hTIFF ) );
597      /* TIFF only contains overviews ? */
598   }
599 #ifdef USE_GDAL
600   CPLPopErrorHandler();
601 #endif
602   return MAPCACHE_FALSE;
603 }
604 
_mapcache_cache_tiff_delete(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)605 static void _mapcache_cache_tiff_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
606 {
607   ctx->set_error(ctx,500,"TIFF cache tile deleting not implemented");
608 }
609 
610 
611 /**
612  * \brief get file content of given tile
613  *
614  * fills the mapcache_tile::data of the given tile with content stored in the file
615  * \private \memberof mapcache_cache_tiff
616  * \sa mapcache_cache::tile_get()
617  */
_mapcache_cache_tiff_get(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)618 static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
619 {
620   char *filename;
621   TIFF *hTIFF = NULL;
622   int rv;
623   mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
624   _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
625   if(GC_HAS_ERROR(ctx)) {
626     return MAPCACHE_FALSE;
627   }
628 #ifdef DEBUG
629   ctx->log(ctx,MAPCACHE_DEBUG,"tile (%d,%d,%d) => filename %s)",
630            tile->x,tile->y,tile->z,filename);
631 #endif
632 
633 #ifdef USE_GDAL
634   CPLPushErrorHandlerEx(mapcache_cache_tiff_gdal_error_handler, ctx);
635 #endif
636 
637   hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r");
638 
639   /*
640    * we currrently have no way of knowing if the opening failed because the tif
641    * file does not exist (which is not an error condition, as it only signals
642    * that the requested tile does not exist in the cache), or if an other error
643    * that should be signaled occurred (access denied, not a tiff file, etc...)
644    *
645    * we ignore this case here and hope that further parts of the code will be
646    * able to detect what's happening more precisely
647    */
648 
649   if(hTIFF) {
650     do {
651       uint32 nSubType = 0;
652       int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */
653       int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */
654 
655       mapcache_grid_level *level;
656       int ntilesx;
657       int ntilesy;
658       toff_t  *offsets=NULL, *sizes=NULL;
659 
660       if( !TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType) )
661         nSubType = 0;
662 
663       /* skip overviews */
664       if( nSubType & FILETYPE_REDUCEDIMAGE )
665         continue;
666 
667 
668 #ifdef DEBUG
669       check_tiff_format(ctx,cache,tile,hTIFF,filename);
670       if(GC_HAS_ERROR(ctx)) {
671         MyTIFFClose(hTIFF);
672 #ifdef USE_GDAL
673         CPLPopErrorHandler();
674 #endif
675         return MAPCACHE_FAILURE;
676       }
677 #endif
678       /*
679        * compute the width and height of the full tiff file. This
680        * is not simply the tile size times the number of tiles per
681        * file for lower zoom levels
682        */
683       level = tile->grid_link->grid->levels[tile->z];
684       ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
685       ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
686 
687       /* x offset of the tile along a row */
688       tiff_offx = tile->x % ntilesx;
689 
690       /*
691        * y offset of the requested row. we inverse it as the rows are ordered
692        * from top to bottom, whereas the tile y is bottom to top
693        */
694       tiff_offy = ntilesy - (tile->y % ntilesy) -1;
695       tiff_off = tiff_offy * ntilesx + tiff_offx;
696 
697       /* get the offset of the jpeg data from the start of the file for each tile */
698       rv = TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &offsets );
699       if( rv != 1 ) {
700         ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" tile offsets",
701                        filename);
702         MyTIFFClose(hTIFF);
703 #ifdef USE_GDAL
704         CPLPopErrorHandler();
705 #endif
706         return MAPCACHE_FAILURE;
707       }
708 
709       /* get the size of the jpeg data for each tile */
710       rv = TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &sizes );
711       if( rv != 1 ) {
712         ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" tile sizes",
713                        filename);
714         MyTIFFClose(hTIFF);
715 #ifdef USE_GDAL
716         CPLPopErrorHandler();
717 #endif
718         return MAPCACHE_FAILURE;
719       }
720 
721       /*
722        * the tile data exists for the given tiff_off if both offsets and size
723        * are not zero for that index.
724        * if not, the tiff file is sparse and is missing the requested tile
725        */
726       if( offsets[tiff_off] > 0 && sizes[tiff_off] >= 2 ) {
727         apr_file_t *f;
728         apr_finfo_t finfo;
729         apr_status_t ret;
730 
731         /* read the jpeg header (common to all tiles) */
732         uint32 jpegtable_size = 0;
733         unsigned char* jpegtable_ptr;
734         rv = TIFFGetField( hTIFF, TIFFTAG_JPEGTABLES, &jpegtable_size, &jpegtable_ptr );
735         if( rv != 1 || !jpegtable_ptr || jpegtable_size < 2) {
736           /* there is no common jpeg header in the tiff tags */
737           ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" jpeg table",
738                          filename);
739           MyTIFFClose(hTIFF);
740 #ifdef USE_GDAL
741           CPLPopErrorHandler();
742 #endif
743           return MAPCACHE_FAILURE;
744         }
745 
746         /*
747          * open the tiff file directly to access the jpeg image data with the given
748          * offset
749          */
750 #ifdef USE_GDAL
751         if( cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
752         {
753           char *bufptr;
754           apr_off_t off;
755           apr_size_t bytes_to_read;
756           size_t bytes_read;
757           VSIStatBufL sStat;
758           VSILFILE* fp;
759 
760           fp = mapcache_cache_tiff_vsi_open(cache, filename, "r");
761           if( fp == NULL )
762           {
763             /*
764              * shouldn't usually happen. we managed to open the file before,
765              * nothing much to do except bail out.
766              */
767             ctx->set_error(ctx,500,
768                            "VSIFOpenL() failed on already open tiff "
769                            "file \"%s\", giving up .... ",
770                            filename);
771             MyTIFFClose(hTIFF);
772             CPLPopErrorHandler();
773             return MAPCACHE_FAILURE;
774           }
775 
776           if( mapcache_cache_tiff_vsi_stat(cache, filename, &sStat) == 0 )  {
777             /*
778              * extract the file modification time. this isn't guaranteed to be the
779              * modification time of the actual tile, but it's the best we can do
780              */
781             tile->mtime = sStat.st_mtime;
782           }
783 
784 #ifdef DEBUG
785           ctx->log(ctx,MAPCACHE_DEBUG,"tile (%d,%d,%d) => mtime = %d)",
786                    tile->x,tile->y,tile->z,tile->mtime);
787 #endif
788 
789 
790           /* create a memory buffer to contain the jpeg data */
791           tile->encoded_data = mapcache_buffer_create(
792               (jpegtable_size+sizes[tiff_off]-4),ctx->pool);
793 
794           /*
795            * copy the jpeg header to the beginning of the memory buffer,
796            * omitting the last 2 bytes
797            */
798           memcpy(tile->encoded_data->buf,jpegtable_ptr,(jpegtable_size-2));
799 
800           /* advance the data pointer to after the header data */
801           bufptr = ((char *)tile->encoded_data->buf) + (jpegtable_size-2);
802 
803 
804           /* go to the specified offset in the tiff file, plus 2 bytes */
805           off = offsets[tiff_off]+2;
806           VSIFSeekL(fp, (vsi_l_offset)off, SEEK_SET);
807 
808           /*
809            * copy the jpeg body at the end of the memory buffer, accounting
810            * for the two bytes we omitted in the previous step
811            */
812           bytes_to_read = sizes[tiff_off]-2;
813           bytes_read = VSIFReadL(bufptr, 1, bytes_to_read, fp);
814 
815           /* check we have correctly read the requested number of bytes */
816           if(bytes_to_read != bytes_read) {
817             ctx->set_error(ctx,500,"failed to read jpeg body in \"%s\".\
818                         (read %d of %d bytes)",
819                         filename,(int)bytes_read,(int)sizes[tiff_off]-2);
820             VSIFCloseL(fp);
821             MyTIFFClose(hTIFF);
822             CPLPopErrorHandler();
823             return MAPCACHE_FAILURE;
824           }
825 
826           tile->encoded_data->size = (jpegtable_size+sizes[tiff_off]-4);
827 
828           VSIFCloseL(fp);
829           CPLPopErrorHandler();
830           return MAPCACHE_SUCCESS;
831         }
832         else
833 #endif
834         if((ret=apr_file_open(&f, filename,
835                               APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT,
836                               ctx->pool)) == APR_SUCCESS) {
837           char *bufptr;
838           apr_off_t off;
839           apr_size_t bytes;
840           ret = apr_file_info_get(&finfo, APR_FINFO_MTIME, f);
841           if(ret == APR_SUCCESS) {
842             /*
843              * extract the file modification time. this isn't guaranteed to be the
844              * modification time of the actual tile, but it's the best we can do
845              */
846             tile->mtime = finfo.mtime;
847           }
848 
849           /* create a memory buffer to contain the jpeg data */
850           tile->encoded_data = mapcache_buffer_create((jpegtable_size+sizes[tiff_off]-4),ctx->pool);
851 
852           /*
853            * copy the jpeg header to the beginning of the memory buffer,
854            * omitting the last 2 bytes
855            */
856           memcpy(tile->encoded_data->buf,jpegtable_ptr,(jpegtable_size-2));
857 
858           /* advance the data pointer to after the header data */
859           bufptr = ((char *)tile->encoded_data->buf) + (jpegtable_size-2);
860 
861 
862           /* go to the specified offset in the tiff file, plus 2 bytes */
863           off = offsets[tiff_off]+2;
864           apr_file_seek(f,APR_SET,&off);
865 
866           /*
867            * copy the jpeg body at the end of the memory buffer, accounting
868            * for the two bytes we omitted in the previous step
869            */
870           bytes = sizes[tiff_off]-2;
871           apr_file_read(f,bufptr,&bytes);
872 
873           /* check we have correctly read the requested number of bytes */
874           if(bytes !=  sizes[tiff_off]-2) {
875             ctx->set_error(ctx,500,"failed to read jpeg body in \"%s\".\
876                         (read %d of %d bytes)", filename,bytes,sizes[tiff_off]-2);
877             apr_file_close(f);
878             MyTIFFClose(hTIFF);
879 #ifdef USE_GDAL
880             CPLPopErrorHandler();
881 #endif
882             return MAPCACHE_FAILURE;
883           }
884 
885           tile->encoded_data->size = (jpegtable_size+sizes[tiff_off]-4);
886 
887           /* finalize and cleanup */
888           apr_file_close(f);
889           MyTIFFClose(hTIFF);
890 #ifdef USE_GDAL
891           CPLPopErrorHandler();
892 #endif
893           return MAPCACHE_SUCCESS;
894         } else {
895           /* shouldn't usually happen. we managed to open the file with TIFFOpen,
896            * but apr_file_open failed to do so.
897            * nothing much to do except bail out.
898            */
899           ctx->set_error(ctx,500,"apr_file_open failed on already open tiff file \"%s\", giving up .... ",
900                          filename);
901           MyTIFFClose(hTIFF);
902 #ifdef USE_GDAL
903           CPLPopErrorHandler();
904 #endif
905           return MAPCACHE_FAILURE;
906         }
907       } else {
908         /* sparse tiff file without the requested tile */
909         MyTIFFClose(hTIFF);
910 #ifdef USE_GDAL
911         CPLPopErrorHandler();
912 #endif
913         return MAPCACHE_CACHE_MISS;
914       }
915     } /* loop through the tiff directories if there are multiple ones */
916     while( TIFFReadDirectory( hTIFF ) );
917 
918     /*
919      * should not happen?
920      * finished looping through directories and didn't find anything suitable.
921      * does the file only contain overviews?
922      */
923     MyTIFFClose(hTIFF);
924   }
925 #ifdef USE_GDAL
926   CPLPopErrorHandler();
927 #endif
928   /* failed to open tiff file */
929   return MAPCACHE_CACHE_MISS;
930 }
931 
932 /**
933  * \brief write tile data to tiff
934  *
935  * writes the content of mapcache_tile::data to tiff.
936  * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked
937  * \returns MAPCACHE_SUCCESS if the tile has been successfully written to tiff
938  * \private \memberof mapcache_cache_tiff
939  * \sa mapcache_cache::tile_set()
940  */
_mapcache_cache_tiff_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)941 static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
942 {
943 #ifdef USE_TIFF_WRITE
944   char *filename;
945   TIFF *hTIFF = NULL;
946   int rv;
947   void *lock;
948   int create;
949   mapcache_cache_tiff *cache;
950   mapcache_image_format_jpeg *format;
951   int tilew;
952   int tileh;
953   unsigned char *rgb;
954   int r,c;
955   apr_finfo_t finfo;
956   mapcache_grid_level *level;
957   int ntilesx;
958   int ntilesy;
959   int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */
960   int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */
961 
962   cache = (mapcache_cache_tiff*)pcache;
963   if( cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
964   {
965     ctx->set_error(ctx,500,"tiff cache %s is read-only\n",pcache->name);
966     return;
967   }
968   _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
969   format = (mapcache_image_format_jpeg*) cache->format;
970   if(GC_HAS_ERROR(ctx)) {
971     return;
972   }
973 #ifdef DEBUG
974   ctx->log(ctx,MAPCACHE_DEBUG,"tile write (%d,%d,%d) => filename %s)",
975            tile->x,tile->y,tile->z,filename);
976 #endif
977 
978   /*
979    * create the directory where the tiff file will be stored
980    */
981   mapcache_make_parent_dirs(ctx,filename);
982   GC_CHECK_ERROR(ctx);
983 
984   tilew = tile->grid_link->grid->tile_sx;
985   tileh = tile->grid_link->grid->tile_sy;
986 
987   if(!tile->raw_image) {
988     tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
989     GC_CHECK_ERROR(ctx);
990   }
991 
992   /* remap xrgb to rgb */
993   rgb = (unsigned char*)malloc(tilew*tileh*3);
994   for(r=0; r<tile->raw_image->h; r++) {
995     unsigned char *imptr = tile->raw_image->data + r * tile->raw_image->stride;
996     unsigned char *rgbptr = rgb + r * tilew * 3;
997     for(c=0; c<tile->raw_image->w; c++) {
998       rgbptr[0] = imptr[2];
999       rgbptr[1] = imptr[1];
1000       rgbptr[2] = imptr[0];
1001       rgbptr += 3;
1002       imptr += 4;
1003     }
1004   }
1005 
1006   /*
1007    * aquire a lock on the tiff file.
1008    */
1009 
1010   while(mapcache_lock_or_wait_for_resource(ctx,(cache->locker?cache->locker:ctx->config->locker),filename, &lock) == MAPCACHE_FALSE);
1011 
1012   /* check if the tiff file exists already */
1013   rv = apr_stat(&finfo,filename,0,ctx->pool);
1014   if(!APR_STATUS_IS_ENOENT(rv)) {
1015     hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r+");
1016     create = 0;
1017   } else {
1018     hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"w+");
1019     create = 1;
1020   }
1021   if(!hTIFF) {
1022     ctx->set_error(ctx,500,"failed to open/create tiff file %s\n",filename);
1023     goto close_tiff;
1024   }
1025 
1026 
1027   /*
1028    * compute the width and height of the full tiff file. This
1029    * is not simply the tile size times the number of tiles per
1030    * file for lower zoom levels
1031    */
1032   level = tile->grid_link->grid->levels[tile->z];
1033   ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
1034   ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
1035   if(create) {
1036 #ifdef USE_GEOTIFF
1037     double  adfPixelScale[3], adfTiePoints[6];
1038     mapcache_extent bbox;
1039     GTIF *gtif;
1040     int x,y;
1041 #endif
1042     /* populate the TIFF tags if we are creating the file */
1043 
1044     TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT );
1045     TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
1046     TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, 8 );
1047     TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_JPEG );
1048     TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
1049     TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, tilew );
1050     TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, tileh );
1051     TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, ntilesx * tilew );
1052     TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, ntilesy * tileh );
1053     TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL,3 );
1054 
1055 #ifdef USE_GEOTIFF
1056     gtif = GTIFNew(hTIFF);
1057     if(gtif) {
1058 
1059       GTIFKeySet(gtif, GTRasterTypeGeoKey, TYPE_SHORT, 1,
1060                  RasterPixelIsArea);
1061 
1062       GTIFKeySet( gtif, GeographicTypeGeoKey, TYPE_SHORT, 1,
1063                   0 );
1064       GTIFKeySet( gtif, GeogGeodeticDatumGeoKey, TYPE_SHORT,
1065                   1, 0 );
1066       GTIFKeySet( gtif, GeogEllipsoidGeoKey, TYPE_SHORT, 1,
1067                   0 );
1068       GTIFKeySet( gtif, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
1069                   0.0 );
1070       GTIFKeySet( gtif, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1,
1071                   0.0 );
1072       switch(tile->grid_link->grid->unit) {
1073         case MAPCACHE_UNIT_FEET:
1074           GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1,
1075                       Linear_Foot );
1076           break;
1077         case MAPCACHE_UNIT_METERS:
1078           GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1,
1079                       Linear_Meter );
1080           break;
1081         case MAPCACHE_UNIT_DEGREES:
1082           GTIFKeySet(gtif, GeogAngularUnitsGeoKey, TYPE_SHORT, 0,
1083                      Angular_Degree );
1084           break;
1085         default:
1086           break;
1087       }
1088 
1089       GTIFWriteKeys(gtif);
1090       GTIFFree(gtif);
1091 
1092       adfPixelScale[0] = adfPixelScale[1] = level->resolution;
1093       adfPixelScale[2] = 0.0;
1094       TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale );
1095 
1096 
1097       /* top left tile x,y */
1098       x = (tile->x / cache->count_x)*(cache->count_x);
1099       y = (tile->y / cache->count_y)*(cache->count_y) + ntilesy - 1;
1100 
1101       mapcache_grid_get_tile_extent(ctx, tile->grid_link->grid,
1102                                x,y,tile->z,&bbox);
1103       adfTiePoints[0] = 0.0;
1104       adfTiePoints[1] = 0.0;
1105       adfTiePoints[2] = 0.0;
1106       adfTiePoints[3] = bbox.minx;
1107       adfTiePoints[4] = bbox.maxy;
1108       adfTiePoints[5] = 0.0;
1109       TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints );
1110     }
1111 
1112 #endif
1113   }
1114   TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, format->quality);
1115   if(format->photometric == MAPCACHE_PHOTOMETRIC_RGB) {
1116     TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
1117   } else {
1118     TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
1119   }
1120   TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
1121 
1122   /* x offset of the tile along a row */
1123   tiff_offx = tile->x % ntilesx;
1124 
1125   /*
1126    * y offset of the requested row. we inverse it as the rows are ordered
1127    * from top to bottom, whereas the tile y is bottom to top
1128    */
1129   tiff_offy = ntilesy - (tile->y % ntilesy) -1;
1130   tiff_off = tiff_offy * ntilesx + tiff_offx;
1131 
1132 
1133   rv = TIFFWriteEncodedTile(hTIFF, tiff_off, rgb, tilew*tileh*3);
1134   free(rgb);
1135   if(!rv) {
1136     ctx->set_error(ctx,500,"failed TIFFWriteEncodedTile to %s",filename);
1137     goto close_tiff;
1138   }
1139   rv = TIFFWriteCheck( hTIFF, 1, "cache_set()");
1140   if(!rv) {
1141     ctx->set_error(ctx,500,"failed TIFFWriteCheck %s",filename);
1142     goto close_tiff;
1143   }
1144 
1145   if(create) {
1146     rv = TIFFWriteDirectory(hTIFF);
1147     if(!rv) {
1148       ctx->set_error(ctx,500,"failed TIFFWriteDirectory to %s",filename);
1149       goto close_tiff;
1150     }
1151   }
1152 
1153 close_tiff:
1154   if(hTIFF)
1155     MyTIFFClose(hTIFF);
1156   mapcache_unlock_resource(ctx,cache->locker?cache->locker:ctx->config->locker, lock);
1157 #else
1158   ctx->set_error(ctx,500,"tiff write support disabled by default");
1159 #endif
1160 
1161 }
1162 
1163 /**
1164  * \private \memberof mapcache_cache_tiff
1165  */
_mapcache_cache_tiff_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * pcache,mapcache_cfg * config)1166 static void _mapcache_cache_tiff_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
1167 {
1168   ezxml_t cur_node;
1169   mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
1170   char * format_name;
1171   mapcache_image_format *pformat;
1172   if ((cur_node = ezxml_child(node,"template")) != NULL) {
1173     char *fmt;
1174     cache->filename_template = apr_pstrdup(ctx->pool,cur_node->txt);
1175     fmt = (char*)ezxml_attr(cur_node,"x_fmt");
1176     if(fmt && *fmt) {
1177       cache->x_fmt = apr_pstrdup(ctx->pool,fmt);
1178     }
1179     fmt = (char*)ezxml_attr(cur_node,"y_fmt");
1180     if(fmt && *fmt) {
1181       cache->y_fmt = apr_pstrdup(ctx->pool,fmt);
1182     }
1183     fmt = (char*)ezxml_attr(cur_node,"z_fmt");
1184     if(fmt && *fmt) {
1185       cache->z_fmt = apr_pstrdup(ctx->pool,fmt);
1186     }
1187     fmt = (char*)ezxml_attr(cur_node,"inv_x_fmt");
1188     if(fmt && *fmt) {
1189       cache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt);
1190     }
1191     fmt = (char*)ezxml_attr(cur_node,"inv_y_fmt");
1192     if(fmt && *fmt) {
1193       cache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt);
1194     }
1195     fmt = (char*)ezxml_attr(cur_node,"div_x_fmt");
1196     if(fmt && *fmt) {
1197       cache->div_x_fmt = apr_pstrdup(ctx->pool,fmt);
1198     }
1199     fmt = (char*)ezxml_attr(cur_node,"div_y_fmt");
1200     if(fmt && *fmt) {
1201       cache->div_y_fmt = apr_pstrdup(ctx->pool,fmt);
1202     }
1203     fmt = (char*)ezxml_attr(cur_node,"inv_div_x_fmt");
1204     if(fmt && *fmt) {
1205       cache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt);
1206     }
1207     fmt = (char*)ezxml_attr(cur_node,"inv_div_y_fmt");
1208     if(fmt && *fmt) {
1209       cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt);
1210     }
1211   }
1212   cur_node = ezxml_child(node,"xcount");
1213   if(cur_node && cur_node->txt && *cur_node->txt) {
1214     char *endptr;
1215     cache->count_x = (int)strtol(cur_node->txt,&endptr,10);
1216     if(*endptr != 0) {
1217       ctx->set_error(ctx,400,"failed to parse xcount value %s for tiff cache %s", cur_node->txt,pcache->name);
1218       return;
1219     }
1220   }
1221   cur_node = ezxml_child(node,"ycount");
1222   if(cur_node && cur_node->txt && *cur_node->txt) {
1223     char *endptr;
1224     cache->count_y = (int)strtol(cur_node->txt,&endptr,10);
1225     if(*endptr != 0) {
1226       ctx->set_error(ctx,400,"failed to parse ycount value %s for tiff cache %s", cur_node->txt,pcache->name);
1227       return;
1228     }
1229   }
1230   cur_node = ezxml_child(node,"format");
1231   if(cur_node && cur_node->txt && *cur_node->txt) {
1232     format_name = cur_node->txt;
1233   } else {
1234     format_name = "JPEG";
1235   }
1236   pformat = mapcache_configuration_get_image_format(
1237               config,format_name);
1238   if(!pformat) {
1239     ctx->set_error(ctx,500,"TIFF cache %s references unknown image format %s",
1240                    pcache->name, format_name);
1241     return;
1242   }
1243   if(pformat->type != GC_JPEG) {
1244     ctx->set_error(ctx,500,"TIFF cache %s can only reference a JPEG image format",
1245                    pcache->name);
1246     return;
1247   }
1248   cache->format = (mapcache_image_format_jpeg*)pformat;
1249 
1250   cur_node = ezxml_child(node,"locker");
1251   if(cur_node) {
1252     mapcache_config_parse_locker(ctx, cur_node, &cache->locker);
1253   }
1254 
1255   cache->storage.type = MAPCACHE_TIFF_STORAGE_FILE;
1256   cur_node = ezxml_child(node,"storage");
1257   if (cur_node) {
1258     ezxml_t child_node;
1259     const char *type = ezxml_attr(cur_node,"type");
1260     if( !type ) {
1261       ctx->set_error(ctx,400,
1262                      "<storage> with no \"type\" attribute in cache (%s)",
1263                      pcache->name);
1264       return;
1265     }
1266 
1267     if( strcmp(type, "rest") == 0 ) {
1268       cache->storage.type = MAPCACHE_TIFF_STORAGE_REST;
1269     }
1270     else if( strcmp(type, "google") == 0 ) {
1271       cache->storage.type = MAPCACHE_TIFF_STORAGE_GOOGLE;
1272       if ((child_node = ezxml_child(cur_node,"access")) != NULL) {
1273         cache->storage.u.google.access =
1274             apr_pstrdup(ctx->pool, child_node->txt);
1275       } else if ( getenv("GS_ACCESS_KEY_ID")) {
1276         cache->storage.u.google.access =
1277             apr_pstrdup(ctx->pool,getenv("GS_ACCESS_KEY_ID"));
1278       } else {
1279         ctx->set_error(ctx,400,
1280                        "google storage in cache (%s) is missing "
1281                        "required <access> child", pcache->name);
1282         return;
1283       }
1284       if ((child_node = ezxml_child(cur_node,"secret")) != NULL) {
1285         cache->storage.u.google.secret =
1286             apr_pstrdup(ctx->pool, child_node->txt);
1287       } else if ( getenv("GS_SECRET_ACCESS_KEY")) {
1288         cache->storage.u.google.access =
1289             apr_pstrdup(ctx->pool,getenv("GS_SECRET_ACCESS_KEY"));
1290       } else {
1291         ctx->set_error(ctx,400,
1292                        "google storage in cache (%s) is missing "
1293                        "required <secret> child", pcache->name);
1294         return;
1295       }
1296     }
1297     else {
1298       ctx->set_error(ctx, 400, "unknown cache type %s for cache \"%s\"",
1299                      type, pcache->name);
1300       return;
1301     }
1302 
1303     if ((child_node = ezxml_child(cur_node,"connection_timeout")) != NULL) {
1304       char *endptr;
1305       cache->storage.connection_timeout = (int)strtol(child_node->txt,&endptr,10);
1306       if(*endptr != 0 || cache->storage.connection_timeout<1) {
1307         ctx->set_error(ctx,400,"invalid rest cache <connection_timeout> "
1308                        "\"%s\" (positive integer expected)",
1309                        child_node->txt);
1310         return;
1311       }
1312     } else {
1313       cache->storage.connection_timeout = 30;
1314     }
1315 
1316     if ((child_node = ezxml_child(cur_node,"timeout")) != NULL) {
1317       char *endptr;
1318       cache->storage.timeout = (int)strtol(child_node->txt,&endptr,10);
1319       if(*endptr != 0 || cache->storage.timeout<1) {
1320         ctx->set_error(ctx,400,"invalid rest cache <timeout> \"%s\" "
1321                        "(positive integer expected)",
1322                        child_node->txt);
1323         return;
1324       }
1325     } else {
1326       cache->storage.timeout = 120;
1327     }
1328 
1329     if ((child_node = ezxml_child(cur_node,"header_file")) != NULL) {
1330       cache->storage.header_file = apr_pstrdup(ctx->pool, child_node->txt);
1331     }
1332 
1333   }
1334 
1335 }
1336 
1337 /**
1338  * \private \memberof mapcache_cache_tiff
1339  */
_mapcache_cache_tiff_configuration_post_config(mapcache_context * ctx,mapcache_cache * pcache,mapcache_cfg * cfg)1340 static void _mapcache_cache_tiff_configuration_post_config(mapcache_context *ctx, mapcache_cache *pcache,
1341     mapcache_cfg *cfg)
1342 {
1343   mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
1344   /* check all required parameters are configured */
1345   if((!cache->filename_template || !strlen(cache->filename_template))) {
1346     ctx->set_error(ctx, 400, "tiff cache %s has no template pattern",cache->cache.name);
1347     return;
1348   }
1349   if(cache->count_x <= 0 || cache->count_y <= 0) {
1350     ctx->set_error(ctx, 400, "tiff cache %s has invalid count (%d,%d)",cache->count_x,cache->count_y);
1351     return;
1352   }
1353 
1354 #ifdef USE_GDAL
1355   if(cache->storage.type == MAPCACHE_TIFF_STORAGE_REST &&
1356      strncmp(cache->filename_template, "http://", 6) != 0 &&
1357      strncmp(cache->filename_template, "https://", 7) != 0 ) {
1358     ctx->set_error(ctx, 400, "tiff cache %s template pattern should begin with http:// or https://",cache->cache.name);
1359     return;
1360   }
1361 
1362   if(cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE &&
1363      strncmp(cache->filename_template, "https://storage.googleapis.com/",
1364              strlen("https://storage.googleapis.com/")) != 0 &&
1365      strncmp(cache->filename_template, "/vsigs/",
1366              strlen("/vsigs/")) != 0 ) {
1367     ctx->set_error(ctx, 400, "tiff cache %s template pattern should begin "
1368                    "with https://storage.googleapis.com/ or /vsigs/",cache->cache.name);
1369     return;
1370   }
1371 #else
1372   if(cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
1373   {
1374     ctx->set_error(ctx, 400, "tiff cache %s cannot use a network based "
1375                    "storage due to mising GDAL dependency",
1376                    cache->cache.name);
1377     return;
1378   }
1379 #endif
1380 }
1381 
1382 /**
1383  * \brief creates and initializes a mapcache_tiff_cache
1384  */
mapcache_cache_tiff_create(mapcache_context * ctx)1385 mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx)
1386 {
1387   mapcache_cache_tiff *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_tiff));
1388   if(!cache) {
1389     ctx->set_error(ctx, 500, "failed to allocate tiff cache");
1390     return NULL;
1391   }
1392   cache->cache.metadata = apr_table_make(ctx->pool,3);
1393   cache->cache.type = MAPCACHE_CACHE_TIFF;
1394   cache->cache._tile_delete = _mapcache_cache_tiff_delete;
1395   cache->cache._tile_get = _mapcache_cache_tiff_get;
1396   cache->cache._tile_exists = _mapcache_cache_tiff_has_tile;
1397   cache->cache._tile_set = _mapcache_cache_tiff_set;
1398   cache->cache.configuration_post_config = _mapcache_cache_tiff_configuration_post_config;
1399   cache->cache.configuration_parse_xml = _mapcache_cache_tiff_configuration_parse_xml;
1400   cache->count_x = 10;
1401   cache->count_y = 10;
1402   cache->x_fmt = cache->y_fmt = cache->z_fmt
1403                                 = cache->inv_x_fmt = cache->inv_y_fmt
1404                                     = cache->div_x_fmt = cache->div_y_fmt
1405                                         = cache->inv_div_x_fmt = cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,"%d");
1406 #ifndef DEBUG
1407   TIFFSetWarningHandler(NULL);
1408   TIFFSetErrorHandler(NULL);
1409 #endif
1410   return (mapcache_cache*)cache;
1411 }
1412 
1413 #else
1414 
mapcache_cache_tiff_create(mapcache_context * ctx)1415 mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx) {
1416   ctx->set_error(ctx,400,"TIFF support not compiled in this version");
1417   return NULL;
1418 }
1419 
1420 #endif
1421 
1422 /* vim: ts=2 sts=2 et sw=2
1423 */
1424