Coverage Report

Created: 2025-06-13 06:18

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