1 /**********************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  OGR Output (for WFS)
6  * Author:   Frank Warmerdam (warmerdam@pobox.com)
7  *
8  **********************************************************************
9  * Copyright (c) 2010, Frank Warmerdam <warmerdam@pobox.com>
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 OR
22  * 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 <assert.h>
31 #include "mapserver.h"
32 #include "mapproject.h"
33 #include "mapthread.h"
34 #include "mapows.h"
35 
36 #include "ogr_api.h"
37 #include "ogr_srs_api.h"
38 #include "cpl_conv.h"
39 #include "cpl_vsi.h"
40 #include "cpl_string.h"
41 
42 #include <string>
43 
44 /************************************************************************/
45 /*                   msInitDefaultOGROutputFormat()                     */
46 /************************************************************************/
47 
msInitDefaultOGROutputFormat(outputFormatObj * format)48 int msInitDefaultOGROutputFormat( outputFormatObj *format )
49 
50 {
51   OGRSFDriverH   hDriver;
52 
53   msOGRInitialize();
54 
55   /* -------------------------------------------------------------------- */
56   /*      check that this driver exists.  Note visiting drivers should    */
57   /*      be pretty threadsafe so don't bother acquiring the GDAL         */
58   /*      lock.                                                           */
59   /* -------------------------------------------------------------------- */
60   hDriver = OGRGetDriverByName( format->driver+4 );
61   if( hDriver == NULL ) {
62     msSetError( MS_MISCERR, "No OGR driver named `%s' available.",
63                 "msInitDefaultOGROutputFormat()", format->driver+4 );
64     return MS_FAILURE;
65   }
66 
67   if( !OGR_Dr_TestCapability( hDriver, ODrCCreateDataSource ) ) {
68     msSetError( MS_MISCERR, "OGR `%s' driver does not support output.",
69                 "msInitDefaultOGROutputFormat()", format->driver+4 );
70     return MS_FAILURE;
71   }
72 
73   /* -------------------------------------------------------------------- */
74   /*      Initialize the object.                                          */
75   /* -------------------------------------------------------------------- */
76   format->imagemode = MS_IMAGEMODE_FEATURE;
77   format->renderer = MS_RENDER_WITH_OGR;
78 
79   /* perhaps we should eventually hardcode mimetypes and extensions
80      for some formats? */
81 
82   return MS_SUCCESS;
83 }
84 
85 /************************************************************************/
86 /*                        msCSLConcatenate()                            */
87 /************************************************************************/
88 
msCSLConcatenate(char ** papszResult,char ** papszToBeAdded)89 static char** msCSLConcatenate( char** papszResult, char** papszToBeAdded )
90 {
91     char** papszIter = papszToBeAdded;
92     while( papszIter && *papszIter )
93     {
94         papszResult = CSLAddString(papszResult, *papszIter);
95         papszIter ++;
96     }
97     return papszResult;
98 }
99 
100 /************************************************************************/
101 /*                       msOGRRecursiveFileList()                       */
102 /*                                                                      */
103 /*      Collect a list of all files under the named directory,          */
104 /*      including those in subdirectories.                              */
105 /************************************************************************/
106 
msOGRRecursiveFileList(const char * path)107 char **msOGRRecursiveFileList( const char *path )
108 {
109   char **file_list;
110   char **result_list = NULL;
111   int i, count, change;
112 
113   file_list = CPLReadDir( path );
114   count = CSLCount(file_list);
115 
116   /* -------------------------------------------------------------------- */
117   /*      Sort the file list so we always get them back in the same       */
118   /*      order - it makes autotests more stable.                         */
119   /* -------------------------------------------------------------------- */
120   do {
121     change = 0;
122     for( i = 0; i < count-1; i++ ) {
123       if( strcasecmp(file_list[i],file_list[i+1]) > 0 ) {
124         char *temp = file_list[i];
125         file_list[i] = file_list[i+1];
126         file_list[i+1] = temp;
127         change = 1;
128       }
129     }
130   } while( change );
131 
132   /* -------------------------------------------------------------------- */
133   /*      collect names we want and process subdirectories.               */
134   /* -------------------------------------------------------------------- */
135   for( i = 0; i < count; i++ ) {
136     char full_filename[MS_MAXPATHLEN];
137     VSIStatBufL  sStatBuf;
138 
139     if( EQUAL(file_list[i],".") || EQUAL(file_list[i],"..") )
140       continue;
141 
142     strlcpy( full_filename,
143              CPLFormFilename( path, file_list[i], NULL ),
144              sizeof(full_filename) );
145 
146     if( VSIStatL( full_filename, &sStatBuf ) != 0 )
147         continue;
148 
149     if( VSI_ISREG( sStatBuf.st_mode ) ) {
150       result_list = CSLAddString( result_list, full_filename );
151     } else if( VSI_ISDIR( sStatBuf.st_mode ) ) {
152       char **subfiles = msOGRRecursiveFileList( full_filename );
153 
154       result_list = msCSLConcatenate( result_list, subfiles );
155 
156       CSLDestroy( subfiles );
157     }
158   }
159 
160   CSLDestroy( file_list );
161 
162   return result_list;
163 }
164 
165 /************************************************************************/
166 /*                           msOGRCleanupDS()                           */
167 /************************************************************************/
msOGRCleanupDS(const char * datasource_name)168 static void msOGRCleanupDS( const char *datasource_name )
169 
170 {
171   char **file_list;
172   char path[MS_MAXPATHLEN];
173   int i;
174   VSIStatBufL sStatBuf;
175 
176   if( VSIStatL( datasource_name, &sStatBuf ) != 0 )
177     return;
178   if( VSI_ISDIR( sStatBuf.st_mode ) )
179     strlcpy( path, datasource_name, sizeof(path) );
180   else
181     strlcpy( path, CPLGetPath( datasource_name ), sizeof(path) );
182 
183   file_list = CPLReadDir( path );
184 
185   for( i = 0; file_list != NULL && file_list[i] != NULL; i++ ) {
186     char full_filename[MS_MAXPATHLEN];
187     VSIStatBufL  sStatBuf;
188 
189     if( EQUAL(file_list[i],".") || EQUAL(file_list[i],"..") )
190       continue;
191 
192     strlcpy( full_filename,
193              CPLFormFilename( path, file_list[i], NULL ),
194              sizeof(full_filename) );
195 
196     if( VSIStatL( full_filename, &sStatBuf ) != 0 )
197       continue;
198 
199     if( VSI_ISREG( sStatBuf.st_mode ) ) {
200       VSIUnlink( full_filename );
201     } else if( VSI_ISDIR( sStatBuf.st_mode ) ) {
202       msOGRCleanupDS( full_filename );
203     }
204   }
205 
206   CSLDestroy( file_list );
207 
208   VSIRmdir( path );
209 }
210 
211 /************************************************************************/
212 /*                          msOGRSetPoints()                            */
213 /************************************************************************/
214 
msOGRSetPoints(OGRGeometryH hGeom,lineObj * line,int bWant2DOutput)215 static void msOGRSetPoints( OGRGeometryH hGeom, lineObj *line, int bWant2DOutput)
216 {
217     int i;
218     if( bWant2DOutput )
219     {
220       for( i = 0; i < line->numpoints; i++ ) {
221         OGR_G_SetPoint_2D( hGeom, i,
222                            line->point[i].x,
223                            line->point[i].y );
224       }
225     }
226     else {
227       for( i = 0; i < line->numpoints; i++ ) {
228         OGR_G_SetPoint( hGeom, i,
229                         line->point[i].x,
230                         line->point[i].y,
231 #ifdef USE_POINT_Z_M
232                         line->point[i].z
233 #else
234                         0.0
235 #endif
236                       );
237       }
238     }
239 }
240 
241 /************************************************************************/
242 /*                          msOGRWriteShape()                           */
243 /************************************************************************/
244 
msOGRWriteShape(layerObj * map_layer,OGRLayerH hOGRLayer,shapeObj * shape,gmlItemListObj * item_list,int nFirstOGRFieldIndex,const char * pszFeatureid)245 static int msOGRWriteShape( layerObj *map_layer, OGRLayerH hOGRLayer,
246                             shapeObj *shape, gmlItemListObj *item_list,
247                             int nFirstOGRFieldIndex, const char *pszFeatureid )
248 
249 {
250   OGRGeometryH hGeom = NULL;
251   OGRFeatureH hFeat;
252   OGRErr eErr;
253   int i, out_field;
254   OGRwkbGeometryType eLayerGType, eFlattenLayerGType;
255   OGRFeatureDefnH hLayerDefn;
256   int bWant2DOutput;
257 
258   hLayerDefn = OGR_L_GetLayerDefn( hOGRLayer );
259   eLayerGType = OGR_FD_GetGeomType(hLayerDefn);
260   eFlattenLayerGType = wkbFlatten(eLayerGType);
261   bWant2DOutput = (eLayerGType == eFlattenLayerGType);
262 
263   /* -------------------------------------------------------------------- */
264   /*      Transform point geometry.                                       */
265   /* -------------------------------------------------------------------- */
266   if( shape->type == MS_SHAPE_POINT ) {
267     OGRGeometryH hMP = NULL;
268     int j;
269 
270     if( shape->numlines < 1 ) {
271       msSetError(MS_MISCERR,
272                  "Failed on odd point geometry.",
273                  "msOGRWriteShape()");
274       return MS_FAILURE;
275     }
276 
277     if( shape->numlines == 1 && shape->line[0].numpoints > 1 )
278     {
279       hGeom = OGR_G_CreateGeometry( wkbMultiPoint );
280       for( j = 0; j < shape->line[0].numpoints; j++ ) {
281         OGRGeometryH hPoint = OGR_G_CreateGeometry( wkbPoint );
282         if( bWant2DOutput ) {
283             OGR_G_SetPoint_2D( hPoint, 0,
284                             shape->line[0].point[j].x,
285                             shape->line[0].point[j].y );
286         }
287         else {
288             OGR_G_SetPoint( hPoint, 0,
289                             shape->line[0].point[j].x,
290                             shape->line[0].point[j].y,
291 #ifdef USE_POINT_Z_M
292                             shape->line[0].point[j].z
293 #else
294                             0.0
295 #endif
296                             );
297         }
298 
299         OGR_G_AddGeometryDirectly( hGeom, hPoint );
300       }
301     }
302     else
303     {
304       if( shape->numlines > 1 )
305         hMP = OGR_G_CreateGeometry( wkbMultiPoint );
306 
307       for( j = 0; j < shape->numlines; j++ ) {
308         if( shape->line[j].numpoints != 1 ) {
309           msSetError(MS_MISCERR,
310                     "Failed on odd point geometry.",
311                     "msOGRWriteShape()");
312           return MS_FAILURE;
313         }
314 
315         hGeom = OGR_G_CreateGeometry( wkbPoint );
316         if( bWant2DOutput ) {
317             OGR_G_SetPoint_2D( hGeom, 0,
318                             shape->line[j].point[0].x,
319                             shape->line[j].point[0].y );
320         }
321         else {
322             OGR_G_SetPoint( hGeom, 0,
323                             shape->line[j].point[0].x,
324                             shape->line[j].point[0].y,
325 #ifdef USE_POINT_Z_M
326                             shape->line[j].point[0].z
327 #else
328                             0.0
329 #endif
330                             );
331         }
332 
333         if( hMP != NULL ) {
334             OGR_G_AddGeometryDirectly( hMP, hGeom );
335         }
336       }
337 
338       if( hMP != NULL )
339         hGeom = hMP;
340     }
341   }
342 
343   /* -------------------------------------------------------------------- */
344   /*      Transform line geometry.                                        */
345   /* -------------------------------------------------------------------- */
346   else if(  shape->type == MS_SHAPE_LINE ) {
347     OGRGeometryH hML = NULL;
348     int j;
349 
350     if( shape->numlines < 1 || shape->line[0].numpoints < 2 ) {
351       msSetError(MS_MISCERR,
352                  "Failed on odd line geometry.",
353                  "msOGRWriteShape()");
354       return MS_FAILURE;
355     }
356 
357     if( shape->numlines > 1 )
358       hML = OGR_G_CreateGeometry( wkbMultiLineString );
359 
360     for( j = 0; j < shape->numlines; j++ ) {
361       hGeom = OGR_G_CreateGeometry( wkbLineString );
362 
363       msOGRSetPoints( hGeom, &(shape->line[j]), bWant2DOutput);
364 
365       if( hML != NULL ) {
366         OGR_G_AddGeometryDirectly( hML, hGeom );
367         hGeom = hML;
368       }
369     }
370   }
371 
372   /* -------------------------------------------------------------------- */
373   /*      Transform polygon geometry.                                     */
374   /* -------------------------------------------------------------------- */
375   else if( shape->type == MS_SHAPE_POLYGON ) {
376     int iRing, iOuter;
377     int *outer_flags;
378     OGRGeometryH hMP;
379 
380     if( shape->numlines < 1 ) {
381       msSetError(MS_MISCERR,
382                  "Failed on odd polygon geometry.",
383                  "msOGRWriteShape()");
384       return MS_FAILURE;
385     }
386 
387     outer_flags = msGetOuterList( shape );
388     hMP = OGR_G_CreateGeometry( wkbMultiPolygon );
389 
390     for( iOuter = 0; iOuter < shape->numlines; iOuter++ ) {
391       int *inner_flags;
392       OGRGeometryH hRing;
393 
394       if( !outer_flags[iOuter] )
395         continue;
396 
397       hGeom = OGR_G_CreateGeometry( wkbPolygon );
398 
399       /* handle outer ring */
400 
401       hRing = OGR_G_CreateGeometry( wkbLinearRing );
402 
403       msOGRSetPoints( hRing, &(shape->line[iOuter]), bWant2DOutput);
404 
405       OGR_G_AddGeometryDirectly( hGeom, hRing );
406 
407 
408       /* handle inner rings (holes) */
409       inner_flags = msGetInnerList( shape, iOuter, outer_flags );
410 
411       for( iRing = 0; iRing < shape->numlines; iRing++ ) {
412         if( !inner_flags[iRing] )
413           continue;
414 
415         hRing = OGR_G_CreateGeometry( wkbLinearRing );
416 
417         msOGRSetPoints( hRing, &(shape->line[iRing]), bWant2DOutput);
418 
419         OGR_G_AddGeometryDirectly( hGeom, hRing );
420       }
421 
422       free(inner_flags);
423 
424       OGR_G_AddGeometryDirectly( hMP, hGeom );
425     }
426 
427     free(outer_flags);
428 
429     if( OGR_G_GetGeometryCount( hMP ) == 1 ) {
430       hGeom = OGR_G_Clone( OGR_G_GetGeometryRef( hMP, 0 ) );
431       OGR_G_DestroyGeometry( hMP );
432     } else {
433       hGeom = hMP;
434     }
435   }
436 
437   /* -------------------------------------------------------------------- */
438   /*      Consider trying to force the geometry to a new type if it       */
439   /*      doesn't match the layer.                                        */
440   /* -------------------------------------------------------------------- */
441   if( hGeom != NULL ) {
442     OGRwkbGeometryType eFlattenFeatureGType =
443         wkbFlatten(OGR_G_GetGeometryType( hGeom ));
444 
445     if( eFlattenFeatureGType != eFlattenLayerGType ) {
446       if( eFlattenLayerGType == wkbPolygon )
447         hGeom = OGR_G_ForceToPolygon( hGeom );
448 
449       else if( eFlattenLayerGType == wkbMultiPolygon )
450         hGeom = OGR_G_ForceToMultiPolygon( hGeom );
451 
452       else if( eFlattenLayerGType == wkbMultiPoint )
453         hGeom = OGR_G_ForceToMultiPoint( hGeom );
454 
455       else if( eFlattenLayerGType == wkbMultiLineString )
456         hGeom = OGR_G_ForceToMultiLineString( hGeom );
457     }
458   }
459 
460   /* -------------------------------------------------------------------- */
461   /*      Consider flattening the geometry to 2D if we want 2D            */
462   /*      output.                                                         */
463   /*      Note: this shouldn't be called in recent OGR versions where     */
464   /*      OGR_G_SetPoint_2D is properly honoured.                         */
465   /* -------------------------------------------------------------------- */
466 
467   if( bWant2DOutput && hGeom != NULL ) {
468     OGRwkbGeometryType eFeatureGType = OGR_G_GetGeometryType( hGeom );
469     if( eFeatureGType != wkbFlatten(eFeatureGType) )
470       OGR_G_FlattenTo2D( hGeom );
471   }
472 
473   /* -------------------------------------------------------------------- */
474   /*      Create the feature, and attach the geometry.                    */
475   /* -------------------------------------------------------------------- */
476   hFeat = OGR_F_Create( hLayerDefn );
477 
478   OGR_F_SetGeometryDirectly( hFeat, hGeom );
479 
480   /* -------------------------------------------------------------------- */
481   /*      Set attributes.                                                 */
482   /* -------------------------------------------------------------------- */
483   out_field = nFirstOGRFieldIndex;
484   for( i = 0; i < item_list->numitems; i++ ) {
485     gmlItemObj *item = item_list->items + i;
486 
487     if(pszFeatureid && !strcmp(pszFeatureid, item->name)) {
488       char *endptr;
489       long feature_id = strtol(shape->values[i],&endptr,10);
490       if(endptr && *endptr==0) {
491         /* only set the featureid if it is numeric */
492         OGR_F_SetFID(hFeat, feature_id);
493       }
494     }
495 
496     if( !item->visible )
497       continue;
498 
499     /* Avoid setting empty strings for numeric fields, so that OGR */
500     /* doesn't take them as 0. (#4633) */
501     if( shape->values[i][0] == '\0' ) {
502       OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn(hLayerDefn, out_field);
503       OGRFieldType eFieldType = OGR_Fld_GetType(hFieldDefn);
504       if( eFieldType == OFTInteger || eFieldType == OFTReal
505           || eFieldType == OFTInteger64
506           )
507       {
508         out_field++;
509         continue;
510       }
511     }
512 
513     OGR_F_SetFieldString( hFeat, out_field++, shape->values[i] );
514   }
515 
516   /* -------------------------------------------------------------------- */
517   /*      Write out and cleanup.                                          */
518   /* -------------------------------------------------------------------- */
519   eErr = OGR_L_CreateFeature( hOGRLayer, hFeat );
520 
521   OGR_F_Destroy( hFeat );
522 
523   if( eErr != OGRERR_NONE ) {
524     msSetError( MS_OGRERR,
525                 "Attempt to write feature failed (code=%d):\n%s",
526                 "msOGRWriteShape()",
527                 (int) eErr,
528                 CPLGetLastErrorMsg() );
529   }
530 
531   if( eErr == OGRERR_NONE )
532     return MS_SUCCESS;
533   else
534     return MS_FAILURE;
535 }
536 
537 /************************************************************************/
538 /*                        msOGRStdoutWriteFunction()                    */
539 /************************************************************************/
540 
541 /* Used by /vsistdout/ */
msOGRStdoutWriteFunction(const void * ptr,size_t size,size_t nmemb,FILE * stream)542 static size_t msOGRStdoutWriteFunction(const void* ptr, size_t size, size_t nmemb, FILE* stream)
543 {
544     msIOContext *ioctx = (msIOContext*) stream;
545     return msIO_contextWrite(ioctx, ptr, size * nmemb ) / size;
546 }
547 
548 /************************************************************************/
549 /*                      msOGROutputGetAdditonalFiles()                  */
550 /*                                                                      */
551 /*  Collect additional files specified in                               */
552 /*  wfs/ows_additional_files_in_output of WEB.METADATA and LAYER.METADATA */
553 /************************************************************************/
554 
555 /* Result to be freed with CSLDestroy() */
msOGROutputGetAdditonalFiles(mapObj * map)556 static char** msOGROutputGetAdditonalFiles( mapObj *map )
557 {
558     int i;
559     hashTableObj* hSetAdditionalFiles;
560     char** papszFiles = NULL;
561 
562     hSetAdditionalFiles = msCreateHashTable();
563 
564     for( i = -1; i < map->numlayers; i++ )
565     {
566         const char* value;
567         if( i < 0 )
568         {
569             value = msOWSLookupMetadata(&(map->web.metadata), "FO", "additional_files_in_output");
570         }
571         else
572         {
573             layerObj *layer = GET_LAYER(map, i);
574             if( !layer->resultcache || layer->resultcache->numresults == 0 )
575                 continue;
576             value = msOWSLookupMetadata(&(layer->metadata), "FO", "additional_files_in_output");
577         }
578 
579         if( value != NULL )
580         {
581             char** papszList = CSLTokenizeString2( value, ",", CSLT_HONOURSTRINGS );
582             char** papszListIter = papszList;
583             while( papszListIter && *papszListIter )
584             {
585                 const char* file = *papszListIter;
586                 VSIStatBufL sStat;
587 
588                 if( strncmp(file, "http://", strlen("http://")) == 0 ||
589                     strncmp(file, "https://", strlen("https://")) == 0 )
590                 {
591                     /* Remote file ? We will use /vsicurl_streaming/ to read it */
592                     if( msLookupHashTable(hSetAdditionalFiles, file) == NULL )
593                     {
594                         msInsertHashTable(hSetAdditionalFiles, file, "YES");
595                         papszFiles = CSLAddString(papszFiles, CPLSPrintf("/vsicurl_streaming/%s", file));
596                     }
597                 }
598                 else
599                 {
600                     int nLen = (int)strlen(file);
601                     char filename[MS_MAXPATHLEN];
602 
603                     if( CPLIsFilenameRelative(file) )
604                     {
605                         if( !map->shapepath )
606                             msTryBuildPath(filename, map->mappath, file);
607                         else
608                             msTryBuildPath3(filename, map->mappath, map->shapepath, file);
609                     }
610                     else
611                         strlcpy(filename, file, MS_MAXPATHLEN);
612 
613                     if( nLen > 2 && (
614                             strcmp(file + nLen - 1, "/") == 0 ||
615                             strcmp(file + nLen - 2, "/*") == 0 ) )
616                     {
617                         *strrchr(filename, '/') = '\0';
618                     }
619                     else if( nLen > 2 && (
620                             strcmp(file + nLen - 1, "\\") == 0 ||
621                             strcmp(file + nLen - 2, "\\*") == 0 ) )
622                     {
623                         *strrchr(filename, '\\') = '\0';
624                     }
625 
626                     if( msLookupHashTable(hSetAdditionalFiles, filename) == NULL )
627                     {
628                         msInsertHashTable(hSetAdditionalFiles, filename, "YES");
629                         if( VSIStatL( filename, &sStat ) == 0 )
630                         {
631                             if( VSI_ISDIR( sStat.st_mode ) )
632                             {
633                                 char** papszDirContent = msOGRRecursiveFileList(filename);
634                                 papszFiles = msCSLConcatenate(papszFiles, papszDirContent);
635                                 CSLDestroy(papszDirContent);
636                             }
637                             else
638                             {
639                                 papszFiles = CSLAddString(papszFiles, filename);
640                             }
641                         }
642                         else
643                         {
644                             msDebug("File %s does not exist.\n", filename);
645                         }
646                     }
647                 }
648 
649                 papszListIter ++;
650             }
651             CSLDestroy(papszList);
652         }
653     }
654 
655     msFreeHashTable(hSetAdditionalFiles);
656 
657     return papszFiles;
658 }
659 
660 /************************************************************************/
661 /*                        msOGRWriteFromQuery()                         */
662 /************************************************************************/
663 
msOGRWriteFromQuery(mapObj * map,outputFormatObj * format,int sendheaders)664 int msOGRWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders )
665 
666 {
667   /* -------------------------------------------------------------------- */
668   /*      Variable declarations.                                          */
669   /* -------------------------------------------------------------------- */
670   OGRSFDriverH hDriver;
671   OGRDataSourceH hDS;
672   const char *storage;
673   const char *fo_filename;
674   const char *form;
675   char datasource_name[MS_MAXPATHLEN];
676   char base_dir[MS_MAXPATHLEN];
677   char *request_dir = NULL;
678   char **ds_options = NULL;
679   char **layer_options = NULL;
680   char **file_list = NULL;
681   int iLayer, i;
682   int bDataSourceNameIsRequestDir = FALSE;
683   int bUseFeatureId = MS_FALSE;
684   const char* pszMatchingFeatures;
685   int nMatchingFeatures = -1;
686   const char* pszFormatName = format->driver+4;
687 
688   pszMatchingFeatures = msGetOutputFormatOption(format, "_matching_features_", "");
689   if( pszMatchingFeatures[0] != '\0' )
690       nMatchingFeatures = atoi(pszMatchingFeatures);
691 
692   /* -------------------------------------------------------------------- */
693   /*      Fetch the output format driver.                                 */
694   /* -------------------------------------------------------------------- */
695   msOGRInitialize();
696 
697   hDriver = OGRGetDriverByName( pszFormatName );
698   if( hDriver == NULL ) {
699     msSetError( MS_MISCERR, "No OGR driver named `%s' available.",
700                 "msOGRWriteFromQuery()", pszFormatName );
701     return MS_FAILURE;
702   }
703 
704   /* -------------------------------------------------------------------- */
705   /*      Capture datasource and layer creation options.                  */
706   /* -------------------------------------------------------------------- */
707   for( i=0; i < format->numformatoptions; i++ ) {
708     if( strncasecmp(format->formatoptions[i],"LCO:",4) == 0 )
709       layer_options = CSLAddString( layer_options,
710                                     format->formatoptions[i] + 4 );
711     if( strncasecmp(format->formatoptions[i],"DSCO:",5) == 0 )
712       ds_options = CSLAddString( ds_options,
713                                  format->formatoptions[i] + 5 );
714   }
715   if( EQUAL(pszFormatName, "GeoJSON") && nMatchingFeatures >= 0 )
716   {
717       const char* pszNativeData =
718         CSLFetchNameValueDef(layer_options, "NATIVE_DATA", "{}");
719       if( pszNativeData[strlen(pszNativeData)-1] == '}' )
720       {
721           std::string tmpl(pszNativeData);
722           tmpl.resize(tmpl.size() - 1);
723           if( strlen(pszNativeData) > 2 )
724               tmpl += ',';
725           tmpl += "\"numberMatched\":";
726           tmpl += std::to_string(nMatchingFeatures);
727           tmpl += '}';
728           layer_options = CSLSetNameValue(layer_options,
729                                           "NATIVE_MEDIA_TYPE",
730                                           "application/vnd.geo+json");
731           layer_options = CSLSetNameValue(layer_options,
732                                           "NATIVE_DATA",
733                                           tmpl.c_str());
734       }
735   }
736   if(!strcasecmp("true",msGetOutputFormatOption(format,"USE_FEATUREID","false"))) {
737     bUseFeatureId = MS_TRUE;
738   }
739 
740   /* ==================================================================== */
741   /*      Determine the output datasource name to use.                    */
742   /* ==================================================================== */
743   storage = msGetOutputFormatOption( format, "STORAGE", "filesystem" );
744   if( EQUAL(storage,"stream") && !msIO_isStdContext() ) {
745     msIOContext *ioctx = msIO_getHandler (stdout);
746     if( ioctx != NULL )
747         VSIStdoutSetRedirection( msOGRStdoutWriteFunction, (FILE*)ioctx );
748     else
749     /* bug #4858, streaming output won't work if standard output has been
750      * redirected, we switch to memory output in this case
751      */
752     storage = "memory";
753   }
754 
755   /* -------------------------------------------------------------------- */
756   /*      Where are we putting stuff?                                     */
757   /* -------------------------------------------------------------------- */
758   if( EQUAL(storage,"filesystem") ) {
759     base_dir[0] = '\0' ;
760   } else if( EQUAL(storage,"memory") ) {
761     strcpy( base_dir, "/vsimem/ogr_out/" );
762   } else if( EQUAL(storage,"stream") ) {
763     /* handled later */
764   } else {
765     msSetError( MS_MISCERR,
766                 "STORAGE=%s value not supported.",
767                 "msOGRWriteFromQuery()",
768                 storage );
769     CSLDestroy(layer_options);
770     CSLDestroy(ds_options);
771     return MS_FAILURE;
772   }
773 
774   /* -------------------------------------------------------------------- */
775   /*      Create a subdirectory to handle this request.                   */
776   /* -------------------------------------------------------------------- */
777   if( !EQUAL(storage,"stream") ) {
778     const char* dir_to_create;
779     if (strlen(base_dir) > 0)
780       request_dir = msTmpFile(map, NULL, base_dir, "" );
781     else
782       request_dir = msTmpFile(map, NULL, NULL, "" );
783 
784     if( request_dir[strlen(request_dir)-1] == '.' )
785       request_dir[strlen(request_dir)-1] = '\0';
786 
787     dir_to_create = request_dir;
788     /* Workaround issue in GDAL versions released at this time :
789      * GDAL issue fixed per https://trac.osgeo.org/gdal/ticket/6991 */
790     if( EQUAL(storage,"memory") && EQUAL(format->driver+4, "ESRI Shapefile") )
791     {
792         dir_to_create = base_dir;
793     }
794 
795     if( VSIMkdir( dir_to_create, 0777 ) != 0 ) {
796       msSetError( MS_MISCERR,
797                   "Attempt to create directory '%s' failed.",
798                   "msOGRWriteFromQuery()",
799                   dir_to_create );
800       msFree(request_dir);
801       CSLDestroy(layer_options);
802       CSLDestroy(ds_options);
803       return MS_FAILURE;
804     }
805   }
806   /*  else handled later */
807 
808   /* -------------------------------------------------------------------- */
809   /*      Setup the full datasource name.                                 */
810   /* -------------------------------------------------------------------- */
811   form = msGetOutputFormatOption( format, "FORM", "zip" );
812 
813   if( EQUAL(form,"zip") )
814     fo_filename = msGetOutputFormatOption( format, "FILENAME", "result.zip" );
815   else
816     fo_filename = msGetOutputFormatOption( format, "FILENAME", "result.dat" );
817 
818   /* Validate that the filename does not contain any directory */
819   /* information, which might lead to removal of unwanted files. (#4086) */
820   if( strchr(fo_filename, '/') != NULL || strchr(fo_filename, ':') != NULL ||
821         strchr(fo_filename, '\\') != NULL ) {
822     msSetError( MS_MISCERR,
823            "Invalid value for FILENAME option. "
824            "It must not contain any directory information.",
825            "msOGRWriteFromQuery()" );
826     msFree(request_dir);
827     CSLDestroy(layer_options);
828     CSLDestroy(ds_options);
829     return MS_FAILURE;
830   }
831 
832   if( !EQUAL(storage,"stream") )
833   {
834     msBuildPath( datasource_name, request_dir, fo_filename );
835 
836     if( EQUAL(form,"zip") )
837     {
838       /* if generating a zip file, remove the zip extension for the internal */
839       /* filename */
840       if( EQUAL(CPLGetExtension(datasource_name), "zip") ) {
841         *strrchr(datasource_name, '.') = '\0';
842       }
843 
844       /* and add .dat extension if user didn't provide another extension */
845       if( EQUAL(CPLGetExtension(datasource_name), "") ) {
846         strcat(datasource_name, ".dat");
847       }
848     }
849 
850     /* Shapefile and MapInfo driver only properly work with multiple layers */
851     /* if the output dataset name is a directory */
852     if( EQUAL(format->driver+4, "ESRI Shapefile") ||
853         EQUAL(format->driver+4, "MapInfo File") )
854     {
855         bDataSourceNameIsRequestDir = TRUE;
856         strcpy(datasource_name, request_dir);
857     }
858   }
859   else
860     strcpy( datasource_name, "/vsistdout/" );
861 
862   msFree( request_dir );
863   request_dir = NULL;
864 
865   /* -------------------------------------------------------------------- */
866   /*      Emit content type headers for stream output now.                */
867   /* -------------------------------------------------------------------- */
868   if( EQUAL(storage,"stream") ) {
869     if( sendheaders && format->mimetype ) {
870       msIO_setHeader("Content-Type","%s",format->mimetype);
871       msIO_sendHeaders();
872     } else
873       msIO_fprintf( stdout, "%c", 10 );
874   }
875 
876   /* ==================================================================== */
877   /*      Create the datasource.                                          */
878   /* ==================================================================== */
879   hDS = OGR_Dr_CreateDataSource( hDriver,  datasource_name, ds_options );
880   CSLDestroy( ds_options );
881 
882   if( hDS == NULL ) {
883     msOGRCleanupDS( datasource_name );
884     msSetError( MS_MISCERR,
885                 "OGR CreateDataSource failed for '%s' with driver '%s'.",
886                 "msOGRWriteFromQuery()",
887                 datasource_name,
888                 format->driver+4 );
889     CSLDestroy(layer_options);
890     return MS_FAILURE;
891   }
892 
893   /* ==================================================================== */
894   /*      Process each layer with a resultset.                            */
895   /* ==================================================================== */
896   for( iLayer = 0; iLayer < map->numlayers; iLayer++ ) {
897     int status = 0;
898     layerObj *layer = GET_LAYER(map, iLayer);
899     shapeObj resultshape;
900     OGRLayerH hOGRLayer;
901     OGRwkbGeometryType eGeomType;
902     OGRSpatialReferenceH srs = NULL;
903     gmlItemListObj *item_list = NULL;
904     const char *value;
905     char *pszWKT;
906     int  nFirstOGRFieldIndex = -1;
907     const char *pszFeatureid = NULL;
908 
909     if( !layer->resultcache )
910       continue;
911 
912     /* -------------------------------------------------------------------- */
913     /*      Will we need to reproject?                                      */
914     /* -------------------------------------------------------------------- */
915     if(layer->transform == MS_TRUE)
916         layer->project = msProjectionsDiffer(&(layer->projection),
917                                &(layer->map->projection));
918 
919     /* -------------------------------------------------------------------- */
920     /*      Establish the geometry type to use for the created layer.       */
921     /*      First we consult the wfs_geomtype field and fallback to         */
922     /*      deriving something from the type of the mapserver layer.        */
923     /* -------------------------------------------------------------------- */
924     value = msOWSLookupMetadata(&(layer->metadata), "FOG", "geomtype");
925     if( value == NULL ) {
926       if( layer->type == MS_LAYER_POINT )
927         value = "Point";
928       else if( layer->type == MS_LAYER_LINE )
929         value = "LineString";
930       else if( layer->type == MS_LAYER_POLYGON )
931         value = "Polygon";
932       else
933         value = "Geometry";
934     }
935 
936     if(bUseFeatureId)
937       pszFeatureid = msOWSLookupMetadata(&(layer->metadata), "FOG", "featureid");
938 
939     if( strcasecmp(value,"Point") == 0 )
940       eGeomType = wkbPoint;
941     else if( strcasecmp(value,"LineString") == 0 )
942       eGeomType = wkbLineString;
943     else if( strcasecmp(value,"Polygon") == 0 )
944       eGeomType = wkbPolygon;
945     else if( strcasecmp(value,"MultiPoint") == 0 )
946       eGeomType = wkbMultiPoint;
947     else if( strcasecmp(value,"MultiLineString") == 0 )
948       eGeomType = wkbMultiLineString;
949     else if( strcasecmp(value,"MultiPolygon") == 0 )
950       eGeomType = wkbMultiPolygon;
951     else if( strcasecmp(value,"GeometryCollection") == 0 )
952       eGeomType = wkbGeometryCollection;
953     else if( strcasecmp(value,"Point25D") == 0 )
954       eGeomType = wkbPoint25D;
955     else if( strcasecmp(value,"LineString25D") == 0 )
956       eGeomType = wkbLineString25D;
957     else if( strcasecmp(value,"Polygon25D") == 0 )
958       eGeomType = wkbPolygon25D;
959     else if( strcasecmp(value,"MultiPoint25D") == 0 )
960       eGeomType = wkbMultiPoint25D;
961     else if( strcasecmp(value,"MultiLineString25D") == 0 )
962       eGeomType = wkbMultiLineString25D;
963     else if( strcasecmp(value,"MultiPolygon25D") == 0 )
964       eGeomType = wkbMultiPolygon25D;
965     else if( strcasecmp(value,"GeometryCollection25D") == 0 )
966       eGeomType = wkbGeometryCollection25D;
967     else if( strcasecmp(value,"Unknown") == 0
968              || strcasecmp(value,"Geometry") == 0 )
969       eGeomType = wkbUnknown;
970     else if( strcasecmp(value,"None") == 0 )
971       eGeomType = wkbNone;
972     else
973       eGeomType = wkbUnknown;
974 
975     /* -------------------------------------------------------------------- */
976     /*      Create a spatial reference.                                     */
977     /* -------------------------------------------------------------------- */
978     pszWKT = msProjectionObj2OGCWKT( &(map->projection) );
979     if( pszWKT != NULL ) {
980       srs = OSRNewSpatialReference( pszWKT );
981       msFree( pszWKT );
982     }
983 
984     /* -------------------------------------------------------------------- */
985     /*      Create the corresponding OGR Layer.                             */
986     /* -------------------------------------------------------------------- */
987     hOGRLayer = OGR_DS_CreateLayer( hDS, layer->name, srs, eGeomType,
988                                     layer_options );
989     if( hOGRLayer == NULL ) {
990       OGR_DS_Destroy( hDS );
991       msOGRCleanupDS( datasource_name );
992       msSetError( MS_MISCERR,
993                   "OGR OGR_DS_CreateLayer failed for layer '%s' with driver '%s'.",
994                   "msOGRWriteFromQuery()",
995                   layer->name,
996                   format->driver+4 );
997       CSLDestroy(layer_options);
998       return MS_FAILURE;
999     }
1000 
1001     if( srs != NULL )
1002       OSRRelease( srs );
1003 
1004     /* -------------------------------------------------------------------- */
1005     /*      Create appropriate attributes on this layer.                    */
1006     /* -------------------------------------------------------------------- */
1007     item_list = msGMLGetItems( layer, "G" );
1008     assert( item_list->numitems == layer->numitems );
1009 
1010     for( i = 0; i < layer->numitems; i++ ) {
1011       OGRFieldDefnH hFldDefn;
1012       OGRErr eErr;
1013       const char *name;
1014       gmlItemObj *item = item_list->items + i;
1015       OGRFieldType eType;
1016 
1017       if( !item->visible )
1018         continue;
1019 
1020       if( item->alias )
1021         name = item->alias;
1022       else
1023         name = item->name;
1024 
1025       if( item->type == NULL )
1026         eType = OFTString;
1027       else if( EQUAL(item->type,"Integer") )
1028         eType = OFTInteger;
1029       else if( EQUAL(item->type,"Long") )
1030         eType = OFTInteger64;
1031       else if( EQUAL(item->type,"Real") )
1032         eType = OFTReal;
1033       else if( EQUAL(item->type,"Character") )
1034         eType = OFTString;
1035       else if( EQUAL(item->type,"Date") )
1036         eType = OFTDate;
1037       else if( EQUAL(item->type,"Time") )
1038         eType = OFTTime;
1039       else if( EQUAL(item->type,"DateTime") )
1040         eType = OFTDateTime;
1041       else if( EQUAL(item->type,"Boolean") )
1042         eType = OFTInteger;
1043       else
1044         eType = OFTString;
1045 
1046       hFldDefn = OGR_Fld_Create( name, eType );
1047 
1048       if( item->width != 0 )
1049         OGR_Fld_SetWidth( hFldDefn, item->width );
1050       if( item->precision != 0 )
1051         OGR_Fld_SetPrecision( hFldDefn, item->precision );
1052 
1053       eErr = OGR_L_CreateField( hOGRLayer, hFldDefn, TRUE );
1054       OGR_Fld_Destroy( hFldDefn );
1055 
1056       if( eErr != OGRERR_NONE ) {
1057         msSetError( MS_OGRERR,
1058                     "Failed to create field '%s' in output feature schema:\n%s",
1059                     "msOGRWriteFromQuery()",
1060                     layer->items[i],
1061                     CPLGetLastErrorMsg() );
1062 
1063         OGR_DS_Destroy( hDS );
1064         msOGRCleanupDS( datasource_name );
1065         msGMLFreeItems(item_list);
1066         CSLDestroy(layer_options);
1067         return MS_FAILURE;
1068       }
1069 
1070       /* The index of the first field we create is not necessarily 0 */
1071       if( nFirstOGRFieldIndex < 0 )
1072           nFirstOGRFieldIndex = OGR_FD_GetFieldCount(
1073                                         OGR_L_GetLayerDefn( hOGRLayer ) ) - 1;
1074     }
1075 
1076     /* -------------------------------------------------------------------- */
1077     /*      Setup joins if needed.  This is likely untested.                */
1078     /* -------------------------------------------------------------------- */
1079     if(layer->numjoins > 0) {
1080       int j;
1081       for(j=0; j<layer->numjoins; j++) {
1082         status = msJoinConnect(layer, &(layer->joins[j]));
1083         if(status != MS_SUCCESS) {
1084           OGR_DS_Destroy( hDS );
1085           msOGRCleanupDS( datasource_name );
1086           msGMLFreeItems(item_list);
1087           CSLDestroy(layer_options);
1088           return status;
1089         }
1090       }
1091     }
1092 
1093     msInitShape( &resultshape );
1094 
1095     /* -------------------------------------------------------------------- */
1096     /*      Loop over all the shapes in the resultcache.                    */
1097     /* -------------------------------------------------------------------- */
1098     for(i=0; i < layer->resultcache->numresults; i++) {
1099 
1100       msFreeShape(&resultshape); /* init too */
1101 
1102       /*
1103       ** Read the shape.
1104       */
1105       if( layer->resultcache->results[i].shape )
1106       {
1107           /* msDebug("Using cached shape %ld\n", layer->resultcache->results[i].shapeindex); */
1108           status = msCopyShape(layer->resultcache->results[i].shape, &resultshape);
1109       }
1110       else
1111       {
1112           status = msLayerGetShape(layer, &resultshape, &(layer->resultcache->results[i]));
1113       }
1114 
1115       if(status != MS_SUCCESS) {
1116           OGR_DS_Destroy( hDS );
1117           msOGRCleanupDS( datasource_name );
1118           msGMLFreeItems(item_list);
1119           msFreeShape(&resultshape);
1120           CSLDestroy(layer_options);
1121           return status;
1122       }
1123 
1124       /*
1125       ** Perform classification, and some annotation related magic.
1126       */
1127       resultshape.classindex =
1128         msShapeGetClass(layer, map, &resultshape, NULL, -1);
1129 
1130       if( resultshape.classindex >= 0
1131           && (layer->_class[resultshape.classindex]->text.string
1132               || layer->labelitem)
1133           && layer->_class[resultshape.classindex]->numlabels > 0
1134           && layer->_class[resultshape.classindex]->labels[0]->size != -1 ) {
1135         resultshape.text = msShapeGetLabelAnnotation(layer,&resultshape,layer->_class[resultshape.classindex]->labels[0]);
1136       }
1137 
1138       /*
1139       ** prepare any necessary JOINs here (one-to-one only)
1140       */
1141       if( layer->numjoins > 0) {
1142         int j;
1143 
1144         for(j=0; j < layer->numjoins; j++) {
1145           if(layer->joins[j].type == MS_JOIN_ONE_TO_ONE) {
1146             msJoinPrepare(&(layer->joins[j]), &resultshape);
1147             msJoinNext(&(layer->joins[j])); /* fetch the first row */
1148           }
1149         }
1150       }
1151 
1152       if( layer->project ) {
1153         if( layer->reprojectorLayerToMap == NULL )
1154         {
1155             layer->reprojectorLayerToMap = msProjectCreateReprojector(
1156                 &layer->projection, &layer->map->projection);
1157         }
1158         if( layer->reprojectorLayerToMap )
1159             status = msProjectShapeEx(layer->reprojectorLayerToMap, &resultshape);
1160         else
1161             status = MS_FAILURE;
1162       }
1163 
1164       /*
1165       ** Write out the feature to OGR.
1166       */
1167 
1168       if( status == MS_SUCCESS )
1169         status = msOGRWriteShape( layer, hOGRLayer, &resultshape,
1170                                   item_list, nFirstOGRFieldIndex, pszFeatureid );
1171 
1172       if(status != MS_SUCCESS) {
1173         OGR_DS_Destroy( hDS );
1174         msOGRCleanupDS( datasource_name );
1175         msGMLFreeItems(item_list);
1176         msFreeShape(&resultshape);
1177         CSLDestroy(layer_options);
1178         return status;
1179       }
1180     }
1181 
1182     msGMLFreeItems(item_list);
1183     msFreeShape(&resultshape); /* init too */
1184   }
1185 
1186   /* -------------------------------------------------------------------- */
1187   /*      Close the datasource.                                           */
1188   /* -------------------------------------------------------------------- */
1189   OGR_DS_Destroy( hDS );
1190 
1191   CSLDestroy( layer_options );
1192 
1193   /* -------------------------------------------------------------------- */
1194   /*      Get list of resulting files.                                    */
1195   /* -------------------------------------------------------------------- */
1196 
1197   if( EQUAL(form,"simple") ) {
1198     file_list = CSLAddString( NULL, datasource_name );
1199   } else {
1200     char datasource_path[MS_MAXPATHLEN];
1201 
1202     if( bDataSourceNameIsRequestDir )
1203         file_list = msOGRRecursiveFileList( datasource_name );
1204     else
1205     {
1206         strncpy( datasource_path, CPLGetPath( datasource_name ), MS_MAXPATHLEN-1 );
1207         file_list = msOGRRecursiveFileList( datasource_path );
1208     }
1209   }
1210 
1211   /* -------------------------------------------------------------------- */
1212   /*      If our "storage" is stream then the output has already been     */
1213   /*      sent back to the client and we don't need to copy it now.       */
1214   /* -------------------------------------------------------------------- */
1215   if( EQUAL(storage,"stream") ) {
1216     /* already done */
1217   }
1218 
1219   /* -------------------------------------------------------------------- */
1220   /*      Handle case of simple file written to stdout.                   */
1221   /* -------------------------------------------------------------------- */
1222   else if( EQUAL(form,"simple") ) {
1223     char buffer[1024];
1224     int  bytes_read;
1225     FILE *fp;
1226     const char *jsonp;
1227 
1228     jsonp = msGetOutputFormatOption( format, "JSONP", NULL );
1229     if( sendheaders ) {
1230       if( !jsonp )
1231       msIO_setHeader("Content-Disposition","attachment; filename=%s",
1232                      CPLGetFilename( file_list[0] ) );
1233       if( format->mimetype )
1234         msIO_setHeader("Content-Type","%s",format->mimetype);
1235       msIO_sendHeaders();
1236     } else
1237       msIO_fprintf( stdout, "%c", 10 );
1238 
1239     fp = VSIFOpenL( file_list[0], "r" );
1240     if( fp == NULL ) {
1241       msSetError( MS_MISCERR,
1242                   "Failed to open result file '%s'.",
1243                   "msOGRWriteFromQuery()",
1244                   file_list[0] );
1245       msOGRCleanupDS( datasource_name );
1246       return MS_FAILURE;
1247     }
1248 
1249     if( jsonp != NULL ) msIO_fprintf( stdout, "%s(", jsonp );
1250 
1251     while( (bytes_read = VSIFReadL( buffer, 1, sizeof(buffer), fp )) > 0 )
1252       msIO_fwrite( buffer, 1, bytes_read, stdout );
1253     VSIFCloseL( fp );
1254 
1255     if (jsonp != NULL) msIO_fprintf( stdout, ");\n" );
1256   }
1257 
1258   /* -------------------------------------------------------------------- */
1259   /*      Handle the case of a multi-part result.                         */
1260   /* -------------------------------------------------------------------- */
1261   else if( EQUAL(form,"multipart") ) {
1262     char **papszAdditionalFiles;
1263     static const char *boundary = "xxOGRBoundaryxx";
1264     msIO_setHeader("Content-Type","multipart/mixed; boundary=%s",boundary);
1265     msIO_sendHeaders();
1266     msIO_fprintf(stdout,"--%s\r\n",boundary );
1267 
1268     papszAdditionalFiles = msOGROutputGetAdditonalFiles(map);
1269     file_list = msCSLConcatenate(file_list, papszAdditionalFiles);
1270     CSLDestroy(papszAdditionalFiles);
1271 
1272     for( i = 0; file_list != NULL && file_list[i] != NULL; i++ ) {
1273       FILE *fp;
1274       int bytes_read;
1275       char buffer[1024];
1276 
1277       if( sendheaders )
1278         msIO_fprintf( stdout,
1279                       "Content-Disposition: attachment; filename=%s\r\n"
1280                       "Content-Type: application/binary\r\n"
1281                       "Content-Transfer-Encoding: binary\r\n\r\n",
1282                       CPLGetFilename( file_list[i] ));
1283 
1284 
1285       fp = VSIFOpenL( file_list[i], "r" );
1286       if( fp == NULL ) {
1287         msSetError( MS_MISCERR,
1288                     "Failed to open result file '%s'.",
1289                     "msOGRWriteFromQuery()",
1290                     file_list[0] );
1291         msOGRCleanupDS( datasource_name );
1292         return MS_FAILURE;
1293       }
1294 
1295       while( (bytes_read = VSIFReadL( buffer, 1, sizeof(buffer), fp )) > 0 )
1296         msIO_fwrite( buffer, 1, bytes_read, stdout );
1297       VSIFCloseL( fp );
1298 
1299       if (file_list[i+1] == NULL)
1300         msIO_fprintf( stdout, "\r\n--%s--\r\n", boundary );
1301       else
1302         msIO_fprintf( stdout, "\r\n--%s\r\n", boundary );
1303     }
1304   }
1305 
1306   /* -------------------------------------------------------------------- */
1307   /*      Handle the case of a zip file result.                           */
1308   /* -------------------------------------------------------------------- */
1309   else if( EQUAL(form,"zip") ) {
1310     FILE *fp;
1311     char *zip_filename = msTmpFile(map, NULL, "/vsimem/ogrzip/", "zip" );
1312     void *hZip;
1313     int bytes_read;
1314     char buffer[1024];
1315     char **papszAdditionalFiles;
1316 
1317     hZip = CPLCreateZip( zip_filename, NULL );
1318 
1319     papszAdditionalFiles = msOGROutputGetAdditonalFiles(map);
1320     file_list = msCSLConcatenate(file_list, papszAdditionalFiles);
1321     CSLDestroy(papszAdditionalFiles);
1322 
1323     for( i = 0; file_list != NULL && file_list[i] != NULL; i++ ) {
1324 
1325       CPLCreateFileInZip( hZip, CPLGetFilename(file_list[i]), NULL );
1326 
1327       fp = VSIFOpenL( file_list[i], "r" );
1328       if( fp == NULL ) {
1329         CPLCloseZip( hZip );
1330         msSetError( MS_MISCERR,
1331                     "Failed to open result file '%s'.",
1332                     "msOGRWriteFromQuery()",
1333                     file_list[0] );
1334         msOGRCleanupDS( datasource_name );
1335         return MS_FAILURE;
1336       }
1337 
1338       while( (bytes_read = VSIFReadL( buffer, 1, sizeof(buffer), fp )) > 0 ) {
1339         CPLWriteFileInZip( hZip, buffer, bytes_read );
1340       }
1341       VSIFCloseL( fp );
1342 
1343       CPLCloseFileInZip( hZip );
1344     }
1345     CPLCloseZip( hZip );
1346 
1347     if( sendheaders ) {
1348       const char* zip_filename = fo_filename;
1349       /* Make sure the filename is ended by .zip */
1350       if( !EQUAL(CPLGetExtension(zip_filename), "zip") &&
1351           !EQUAL(CPLGetExtension(zip_filename), "kmz") )
1352           zip_filename = CPLFormFilename(NULL, fo_filename, "zip");
1353       msIO_setHeader("Content-Disposition","attachment; filename=%s",zip_filename);
1354       msIO_setHeader("Content-Type","application/zip");
1355       msIO_sendHeaders();
1356     }
1357 
1358     fp = VSIFOpenL( zip_filename, "r" );
1359     if( fp == NULL ) {
1360       msSetError( MS_MISCERR,
1361                   "Failed to open zip file '%s'.",
1362                   "msOGRWriteFromQuery()",
1363                   file_list[0] );
1364       msOGRCleanupDS( datasource_name );
1365       return MS_FAILURE;
1366     }
1367 
1368     while( (bytes_read = VSIFReadL( buffer, 1, sizeof(buffer), fp )) > 0 )
1369       msIO_fwrite( buffer, 1, bytes_read, stdout );
1370     VSIFCloseL( fp );
1371 
1372     msFree( zip_filename );
1373   }
1374 
1375   /* -------------------------------------------------------------------- */
1376   /*      Handle illegal form value.                                      */
1377   /* -------------------------------------------------------------------- */
1378   else {
1379     msSetError( MS_MISCERR, "Unsupported FORM=%s value.",
1380                 "msOGRWriteFromQuery()", form );
1381     msOGRCleanupDS( datasource_name );
1382     return MS_FAILURE;
1383   }
1384 
1385   msOGRCleanupDS( datasource_name );
1386 
1387   CSLDestroy( file_list );
1388 
1389   return MS_SUCCESS;
1390 }
1391 
1392 /************************************************************************/
1393 /*                     msPopulateRenderVTableOGR()                      */
1394 /************************************************************************/
1395 
msPopulateRendererVTableOGR(rendererVTableObj * renderer)1396 int msPopulateRendererVTableOGR( rendererVTableObj *renderer )
1397 {
1398   /* we aren't really a normal renderer so we leave everything default */
1399   return MS_SUCCESS;
1400 }
1401